eventing, renaming, subselecting
This commit is contained in:
@@ -11,11 +11,11 @@ Authors = Authors
|
|||||||
Order = Order
|
Order = Order
|
||||||
Orders = Orders
|
Orders = Orders
|
||||||
Price = Price
|
Price = Price
|
||||||
ShippingAddress = Shipping Address
|
shippingAddress = Shipping Address
|
||||||
CityName = City Name
|
cityName = City Name
|
||||||
HouseNumber = House Number
|
houseNumber = House Number
|
||||||
StreetName = Street Name
|
streetName = Street Name
|
||||||
PostalCode = Postal Code
|
postalCode = Postal Code
|
||||||
Country = Country
|
country = Country
|
||||||
AddressID = Address ID
|
addressID = Address ID
|
||||||
BusinessPartner = Business Partner
|
businessPartner = Business Partner
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ annotate my.Authors with {
|
|||||||
}
|
}
|
||||||
|
|
||||||
annotate my.ShippingAddresses with {
|
annotate my.ShippingAddresses with {
|
||||||
AddressID @title:'{i18n>AddressID}';
|
addressID @title:'{i18n>addressID}';
|
||||||
BusinessPartner @title:'{i18n>BusinessPartner}';
|
businessPartner @title:'{i18n>businessPartner}';
|
||||||
CityName @title:'{i18n>CityName}';
|
cityName @title:'{i18n>cityName}';
|
||||||
StreetName @title:'{i18n>StreetName}';
|
streetName @title:'{i18n>streetName}';
|
||||||
PostalCode @title:'{i18n>PostalCode}';
|
postalCode @title:'{i18n>postalCode}';
|
||||||
Country @title:'{i18n>Country}';
|
country @title:'{i18n>country}';
|
||||||
HouseNumber @title:'{i18n>HouseNumber}';
|
houseNumber @title:'{i18n>houseNumber}';
|
||||||
}
|
}
|
||||||
@@ -29,23 +29,24 @@ annotate AdminService.Orders with {
|
|||||||
Label: 'Addresses',
|
Label: 'Addresses',
|
||||||
SearchSupported: 'true',
|
SearchSupported: 'true',
|
||||||
Parameters: [
|
Parameters: [
|
||||||
{ $Type: 'Common.ValueListParameterOut', LocalDataProperty: 'shippingAddress_AddressID', ValueListProperty: 'AddressID'},
|
{ $Type: 'Common.ValueListParameterOut', LocalDataProperty: 'shippingAddress_addressID', ValueListProperty: 'addressID'},
|
||||||
{ $Type: 'Common.ValueListParameterOut', LocalDataProperty: 'shippingAddress_BusinessPartner', ValueListProperty: 'BusinessPartner'},
|
{ $Type: 'Common.ValueListParameterOut', LocalDataProperty: 'shippingAddress_businessPartner', ValueListProperty: 'businessPartner'},
|
||||||
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PostalCode'},
|
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'postalCode'},
|
||||||
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CityName'},
|
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'cityName'},
|
||||||
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'StreetName'},
|
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'country'},
|
||||||
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'HouseNumber'},
|
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'streetName'},
|
||||||
|
{ $Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'houseNumber'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
SideEffects : {
|
SideEffects : {
|
||||||
EffectTypes : #ValueChange,
|
EffectTypes : #ValueChange,
|
||||||
SourceProperties : [shippingAddress_AddressID],
|
SourceProperties : [shippingAddress_addressID],
|
||||||
TargetProperties : [
|
TargetProperties : [
|
||||||
shippingAddress.Country,
|
shippingAddress.country,
|
||||||
shippingAddress.HouseNumber,
|
shippingAddress.houseNumber,
|
||||||
shippingAddress.StreetName,
|
shippingAddress.streetName,
|
||||||
shippingAddress.CityName,
|
shippingAddress.cityName,
|
||||||
shippingAddress.PostalCode
|
shippingAddress.postalCode
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,11 +115,11 @@ annotate AdminService.Orders with @(
|
|||||||
},
|
},
|
||||||
FieldGroup#ShippingAddress: {
|
FieldGroup#ShippingAddress: {
|
||||||
Data: [
|
Data: [
|
||||||
{Value: shippingAddress_AddressID, Label:'{i18n>ShippingAddress}'},
|
{Value: shippingAddress_addressID, Label:'{i18n>shippingAddress}'},
|
||||||
{Value: shippingAddress.HouseNumber, Label:'{i18n>HouseNumber}'},
|
{Value: shippingAddress.houseNumber, Label:'{i18n>houseNumber}'},
|
||||||
{Value: shippingAddress.StreetName, Label:'{i18n>StreetName}'},
|
{Value: shippingAddress.streetName, Label:'{i18n>streetName}'},
|
||||||
{Value: shippingAddress.CityName, Label:'{i18n>CityName}'},
|
{Value: shippingAddress.cityName, Label:'{i18n>cityName}'},
|
||||||
{Value: shippingAddress.PostalCode, Label:'{i18n>PostalCode}'},
|
{Value: shippingAddress.postalCode, Label:'{i18n>postalCode}'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,29 +37,23 @@ entity OrderItems : cuid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use external information
|
// TODO: Use external information
|
||||||
// @cds.persistence.skip: false
|
@cds.persistence.skip: false
|
||||||
// @cds.persistence.table
|
@cds.persistence.table
|
||||||
// entity ShippingAddresses as projection on extAddresses {
|
entity ShippingAddresses as projection on extAddresses {
|
||||||
// key AddressID,
|
key AddressID as addressID,
|
||||||
// key BusinessPartner,
|
key BusinessPartner as businessPartner,
|
||||||
// Country,
|
Country as country,
|
||||||
// CityName,
|
CityName as cityName,
|
||||||
// PostalCode,
|
PostalCode as postalCode,
|
||||||
// StreetName,
|
StreetName as streetName,
|
||||||
// HouseNumber
|
HouseNumber as houseNumber
|
||||||
// // key AddressID: String;
|
|
||||||
// // Country: String @readonly;
|
|
||||||
// // CityName: String @readonly;
|
|
||||||
// // PostalCode: String @readonly;
|
|
||||||
// // StreetName: String @readonly;
|
|
||||||
// // HouseNumber: String @readonly;
|
|
||||||
// }
|
|
||||||
entity ShippingAddresses {
|
|
||||||
key AddressID: String;
|
|
||||||
key BusinessPartner: String;
|
|
||||||
Country: String @readonly;
|
|
||||||
CityName: String @readonly;
|
|
||||||
PostalCode: String @readonly;
|
|
||||||
StreetName: String @readonly;
|
|
||||||
HouseNumber: String @readonly;
|
|
||||||
}
|
}
|
||||||
|
// entity ShippingAddresses {
|
||||||
|
// key AddressID: String;
|
||||||
|
// key BusinessPartner: String;
|
||||||
|
// Country: String @readonly;
|
||||||
|
// CityName: String @readonly;
|
||||||
|
// PostalCode: String @readonly;
|
||||||
|
// StreetName: String @readonly;
|
||||||
|
// HouseNumber: String @readonly;
|
||||||
|
// }
|
||||||
|
|||||||
@@ -17,16 +17,27 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"API_BUSINESS_PARTNER": {
|
"API_BUSINESS_PARTNER": {
|
||||||
"kind": "odata",
|
"kind": "odata",
|
||||||
"model": "srv/external/API_BUSINESS_PARTNER"
|
"model": "srv/external/API_BUSINESS_PARTNER",
|
||||||
|
"--credentials": {
|
||||||
|
"url": "http://www.google.com"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"messaging": {
|
"messaging": {
|
||||||
"kind": "enterprise-messaging"
|
"kind": "file-based-messaging"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"passport": {
|
"passport": {
|
||||||
"strategy": "mock",
|
"strategy": "mock",
|
||||||
"users": { "alice": { "roles": [ "admin" ], "password": "secret", "ID": "ALICE" } }
|
"users": {
|
||||||
|
"alice": {
|
||||||
|
"roles": [
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
"password": "secret",
|
||||||
|
"ID": "ALICE"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
PATCH http://localhost:4004/api-business-partner/A_BusinessPartnerAddress(BusinessPartner='ANONYMOUS',AddressID='62640')
|
PATCH http://localhost:4004/api-business-partner/A_BusinessPartnerAddress(BusinessPartner='ALICE',AddressID='62640')
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
Authorization: Basic QUxJQ0Utc2VjcmV0
|
||||||
|
|
||||||
{
|
{
|
||||||
"PostalCode": "123456"
|
"PostalCode": "123456"
|
||||||
|
|||||||
@@ -1,133 +1,135 @@
|
|||||||
const cds = require('@sap/cds')
|
const cds = require('@sap/cds')
|
||||||
const { Books, ShippingAddresses, Orders } = cds.entities
|
const { Books, ShippingAddresses } = cds.entities
|
||||||
const RELEVANT_ADDRESS_COLUMNS = [
|
|
||||||
'AddressID',
|
|
||||||
'BusinessPartner',
|
|
||||||
'CityName',
|
|
||||||
'StreetName',
|
|
||||||
'PostalCode',
|
|
||||||
'Country',
|
|
||||||
'HouseNumber'
|
|
||||||
]
|
|
||||||
|
|
||||||
const bupaSrv = cds.connect.to('API_BUSINESS_PARTNER')
|
const bupaSrv = cds.connect.to('API_BUSINESS_PARTNER')
|
||||||
const messagingSrv = cds.connect.to('messaging')
|
|
||||||
|
|
||||||
messagingSrv.on('sap/messaging/ccf/BO/BusinessPartner/Changed', async msg => {
|
const _diff = (obj1, obj2) =>
|
||||||
|
Object.keys(obj1).reduce(
|
||||||
|
(res, curr) =>
|
||||||
|
obj1[curr] === obj2[curr] ? res : (res[curr] = obj2[curr]) && res,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
const _qlsToUpdateDifferences = (ownAddresses, remoteAddresses) =>
|
||||||
|
ownAddresses
|
||||||
|
.map(ownAddress => {
|
||||||
|
const remoteAddress = remoteAddresses.find(
|
||||||
|
address =>
|
||||||
|
address.businessPartner === ownAddress.businessPartner &&
|
||||||
|
address.addressID === ownAddress.addressID
|
||||||
|
)
|
||||||
|
if (remoteAddress) {
|
||||||
|
const diff = _diff(ownAddress, remoteAddress)
|
||||||
|
console.log('changing', diff)
|
||||||
|
return (
|
||||||
|
Object.keys(diff).length &&
|
||||||
|
UPDATE(ShippingAddresses)
|
||||||
|
.set(diff)
|
||||||
|
.where({
|
||||||
|
businessPartner: ownAddress.businessPartner,
|
||||||
|
addressID: ownAddress.addressID
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return DELETE(ShippingAddresses).where({
|
||||||
|
businessPartner: ownAddress.businessPartner,
|
||||||
|
addressID: ownAddress.addressID
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.filter(el => el)
|
||||||
|
|
||||||
|
bupaSrv.on('sap/messaging/ccf/BO/BusinessPartner/Changed', async msg => {
|
||||||
console.log('>> Message:', msg.data)
|
console.log('>> Message:', msg.data)
|
||||||
const BusinessPartner = msg.data.KEY[0].BUSINESSPARTNER
|
const businessPartner = msg.data.KEY[0].BUSINESSPARTNER
|
||||||
// TODO: Remove toLower hack.
|
const tx = cds.transaction()
|
||||||
// Every BusinessPartner from S/4HANA is UPPERCASE.
|
const selectQl = SELECT.from(ShippingAddresses).where({ businessPartner })
|
||||||
const ownOrders = await cds.run(SELECT.from(Orders).where('createdBy like', BusinessPartner))
|
const selectQlToBeDeleted = SELECT.from(ShippingAddresses).where({
|
||||||
console.log(ownOrders)
|
businessPartner
|
||||||
// const ownAddresses = await cds.run(
|
})
|
||||||
// SELECT(['AddressID'])
|
|
||||||
// .from(ShippingAddresses)
|
const ownAddresses = await tx.run(selectQl)
|
||||||
// .where({ BusinessPartner: businessPartner })
|
await tx.commit()
|
||||||
// )
|
console.log('own:', ownAddresses)
|
||||||
// if (ownAddresses && ownAddresses.length > 0) {
|
if (ownAddresses && ownAddresses.length > 0) {
|
||||||
// console.log('found business partner', businessPartner)
|
console.log('found')
|
||||||
// }
|
const txExt = bupaSrv.transaction()
|
||||||
// const tx = bupaSrv.transaction()
|
const remoteAddresses = await txExt.run(selectQlToBeDeleted)
|
||||||
// const remoteAddresses = await Promise.all(
|
|
||||||
// ownAddresses.map(addressResult => {
|
await _qlsToUpdateDifferences(ownAddresses, remoteAddresses).map(ql =>
|
||||||
// return tx.run(
|
tx.run(ql)
|
||||||
// SELECT.one.from('API_BUSINESS_PARTNER.A_BusinessPartnerAddress')
|
)
|
||||||
// .columns(RELEVANT_ADDRESS_COLUMNS)
|
await tx.commit()
|
||||||
// .where({
|
}
|
||||||
// AddressID: addressResult.AddressID,
|
|
||||||
// BusinessPartner: businessPartner
|
|
||||||
// })
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
// )
|
|
||||||
// console.log('addresses found:', remoteAddresses)
|
|
||||||
// await Promise.all(remoteAddresses.map(address => {
|
|
||||||
// console.log('updating', address)
|
|
||||||
// if (address) {
|
|
||||||
// return cds.run(UPDATE(ShippingAddresses).set(address))
|
|
||||||
// }
|
|
||||||
// }))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/** Service implementation for CatalogService */
|
|
||||||
module.exports = cds.service.impl(function () {
|
module.exports = cds.service.impl(function () {
|
||||||
|
async function _readAddresses (req) {
|
||||||
|
console.log('Addresses', ShippingAddresses)
|
||||||
|
const businessPartner = req.user.id
|
||||||
|
const tx = bupaSrv.transaction(req)
|
||||||
|
const ql = SELECT.from(ShippingAddresses).where({
|
||||||
|
businessPartner
|
||||||
|
})
|
||||||
|
if (req.query && req.query.SELECT && req.query.SELECT.columns) {
|
||||||
|
ql.columns(req.query.SELECT.columns)
|
||||||
|
}
|
||||||
|
if (req.query && req.query.SELECT && req.query.SELECT.where) {
|
||||||
|
ql.where(req.query.SELECT.where)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await tx.run(ql)
|
||||||
|
delete result.businessPartner
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _fillAddress (req) {
|
||||||
|
if (req.data.shippingAddress_addressID) {
|
||||||
|
const businessPartner = req.user.id
|
||||||
|
const tx = bupaSrv.transaction(req)
|
||||||
|
const response = await tx.run(
|
||||||
|
SELECT.from(ShippingAddresses).where({
|
||||||
|
addressID: req.data.shippingAddress_addressID,
|
||||||
|
businessPartner
|
||||||
|
})
|
||||||
|
)
|
||||||
|
if (response && response.length > 0) {
|
||||||
|
const tx2 = cds.transaction(req)
|
||||||
|
try {
|
||||||
|
const qlStatement = INSERT.into(ShippingAddresses).entries(response)
|
||||||
|
await tx2.run(qlStatement)
|
||||||
|
} catch (e) {
|
||||||
|
// already in there
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.error('Shipping address not found.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _reduceStock (req) {
|
||||||
|
const { Items: OrderItems } = req.data
|
||||||
|
if (OrderItems && OrderItems.length > 0) {
|
||||||
|
const all = await cds.transaction(req).run(() =>
|
||||||
|
OrderItems.map(order =>
|
||||||
|
UPDATE(Books)
|
||||||
|
.set('stock -=', order.amount)
|
||||||
|
.where('ID =', order.book_ID)
|
||||||
|
.and('stock >=', order.amount)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
all.forEach((affectedRows, i) => {
|
||||||
|
if (affectedRows === 0)
|
||||||
|
req.error(
|
||||||
|
409,
|
||||||
|
`${OrderItems[i].amount} exceeds stock for book #${
|
||||||
|
OrderItems[i].book_ID
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.before('CREATE', 'Orders', _reduceStock)
|
this.before('CREATE', 'Orders', _reduceStock)
|
||||||
this.before('PATCH', 'Orders', _fillAddress)
|
this.before('PATCH', 'Orders', _fillAddress)
|
||||||
this.on('READ', 'Addresses', _readAddresses)
|
this.on('READ', 'Addresses', _readAddresses)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function _readAddresses (req) {
|
|
||||||
const businessPartner = req.user.id
|
|
||||||
if (!businessPartner) {
|
|
||||||
return req.reject('You need to be authorized.')
|
|
||||||
}
|
|
||||||
const tx = bupaSrv.transaction(req)
|
|
||||||
const ql = SELECT.from('API_BUSINESS_PARTNER.A_BusinessPartnerAddress').where(
|
|
||||||
{ BusinessPartner: businessPartner }
|
|
||||||
)
|
|
||||||
if (req.query && req.query.SELECT && req.query.SELECT.columns) {
|
|
||||||
ql.columns(req.query.SELECT.columns)
|
|
||||||
} else {
|
|
||||||
ql.columns(RELEVANT_ADDRESS_COLUMNS)
|
|
||||||
}
|
|
||||||
if (req.query && req.query.SELECT && req.query.SELECT.where) {
|
|
||||||
ql.where(req.query.SELECT.where)
|
|
||||||
}
|
|
||||||
const result = await tx.run(ql)
|
|
||||||
delete result.BusinessPartner
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Fill Address data from external service */
|
|
||||||
async function _fillAddress (req) {
|
|
||||||
if (req.data.shippingAddress_AddressID) {
|
|
||||||
const businessPartner = req.user.id
|
|
||||||
if (!businessPartner) {
|
|
||||||
return req.reject('You need to be authorized.')
|
|
||||||
}
|
|
||||||
const tx = bupaSrv.transaction(req)
|
|
||||||
const response = await tx.run(
|
|
||||||
SELECT.from('API_BUSINESS_PARTNER.A_BusinessPartnerAddress')
|
|
||||||
.columns(RELEVANT_ADDRESS_COLUMNS)
|
|
||||||
.where({
|
|
||||||
AddressID: req.data.shippingAddress_AddressID,
|
|
||||||
BusinessPartner: businessPartner
|
|
||||||
})
|
|
||||||
)
|
|
||||||
if (response && response.length > 0) {
|
|
||||||
const tx2 = cds.transaction(req)
|
|
||||||
try {
|
|
||||||
await tx2.run(INSERT.into(ShippingAddresses).entries(response))
|
|
||||||
} catch (e) {
|
|
||||||
// already in there
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
req.error('Shipping address not found.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reduce stock of ordered books if available stock suffices */
|
|
||||||
async function _reduceStock (req) {
|
|
||||||
const { Items: OrderItems } = req.data
|
|
||||||
if (OrderItems && OrderItems.length > 0) {
|
|
||||||
const all = await cds.transaction(req).run(() =>
|
|
||||||
OrderItems.map(order =>
|
|
||||||
UPDATE(Books)
|
|
||||||
.set('stock -=', order.amount)
|
|
||||||
.where('ID =', order.book_ID)
|
|
||||||
.and('stock >=', order.amount)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
all.forEach((affectedRows, i) => {
|
|
||||||
if (affectedRows === 0)
|
|
||||||
req.error(
|
|
||||||
409,
|
|
||||||
`${OrderItems[i].amount} exceeds stock for book #${
|
|
||||||
OrderItems[i].book_ID
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module.exports = srv => {
|
|||||||
srv.on('UPDATE', req => {
|
srv.on('UPDATE', req => {
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
KEY: [{ BUSINESSPARTNER: req.user.id }]
|
KEY: [{ BUSINESSPARTNER: req.data.BusinessPartner }]
|
||||||
}
|
}
|
||||||
console.log('<< Message:', payload)
|
console.log('<< Message:', payload)
|
||||||
srv.emit('sap/messaging/ccf/BO/BusinessPartner/Changed', payload)
|
srv.emit('sap/messaging/ccf/BO/BusinessPartner/Changed', payload)
|
||||||
|
|||||||
Reference in New Issue
Block a user