eventing, renaming, subselecting

This commit is contained in:
D065023
2019-12-11 11:12:57 +01:00
parent e6d5183cce
commit ddd02b52f2
8 changed files with 193 additions and 184 deletions

View File

@@ -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

View File

@@ -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}';
} }

View File

@@ -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}'},
] ]
}, },
}, },

View File

@@ -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;
// }

View File

@@ -17,17 +17,28 @@
"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"
}
}
} }
} }
} }
} }

View File

@@ -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"

View File

@@ -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
}`
)
})
}
}

View File

@@ -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)