diff --git a/plugins/beckn-bpp-adapter/server/mappings/on_confirm.jsonata b/plugins/beckn-bpp-adapter/server/mappings/on_confirm.jsonata index aaa9b11a..3da19ff8 100644 --- a/plugins/beckn-bpp-adapter/server/mappings/on_confirm.jsonata +++ b/plugins/beckn-bpp-adapter/server/mappings/on_confirm.jsonata @@ -1,3 +1,5 @@ +( + $itemBody := message[0].itemsBody; { "context": $context(context, $action), "message": { @@ -51,6 +53,9 @@ "value": $string(stock_quantity ? stock_quantity : 0), "unit": quantity_unit } + }, + "selected": { + "count": $itemQuantity($itemBody, %.id) } }, "category_ids": $map( @@ -94,7 +99,7 @@ "duration": service.time_slot_per_client_in_min ? $string(service.time_slot_per_client_in_min) : "" } }[], - "quote": $quote(message[0].items), + "quote": $quotePrice(message[0].items, message[0].itemsBody, context), "billing": message.billing, "fulfillments": $map(message.fulfillment, function($elem) { $merge([$elem, {"state":{"descriptor": { @@ -107,4 +112,5 @@ "cancellation_terms": $cancellationTerms(message[0].items) } } -} \ No newline at end of file +} +) \ No newline at end of file diff --git a/plugins/beckn-bpp-adapter/server/mappings/on_init.jsonata b/plugins/beckn-bpp-adapter/server/mappings/on_init.jsonata index ffe09e7b..27f808af 100644 --- a/plugins/beckn-bpp-adapter/server/mappings/on_init.jsonata +++ b/plugins/beckn-bpp-adapter/server/mappings/on_init.jsonata @@ -1,99 +1,105 @@ -{ - "context": $context(context, $action), - "message": { - "order": { - "provider": message.{ - "id": $string(id), - "descriptor": { - "name": provider_name, - "short_desc": short_desc ? short_desc : "", - "long_desc": long_desc ? long_desc : "", - "additional_desc": { - "url": provider_uri +( + $itemBody := message[0].itemsBody; + { + "context": $context(context, $action), + "message": { + "order": { + "provider": message.{ + "id": $string(id), + "descriptor": { + "name": provider_name, + "short_desc": short_desc ? short_desc : "", + "long_desc": long_desc ? long_desc : "", + "additional_desc": { + "url": provider_uri + }, + "images": items.image.{ + "url": url ? url : "https://abc.com", + "size_type": size_type ? size_type : "sm" + }[] }, - "images": items.image.{ - "url": url ? url : "https://abc.com", - "size_type": size_type ? size_type : "sm" - }[] + "categories": category_ids. { + "id": $string(id), + "descriptor": { + "name": value + } + }[], + "rating": provider_rating, + "short_desc": short_desc, + "fulfillments": $fulfillments(fulfillments, items), + "locations": $locations($append(location_id, [items.item_fulfillment_ids.location_id])), + "rateable": true }, - "categories": category_ids. { + "items": message.items.{ "id": $string(id), "descriptor": { - "name": value - } - }[], - "rating": provider_rating, - "short_desc": short_desc, - "fulfillments": $fulfillments(fulfillments, items), - "locations": $locations($append(location_id, [items.item_fulfillment_ids.location_id])), - "rateable": true - }, - "items": message.items.{ - "id": $string(id), - "descriptor": { - "name": name ? name : "", - "code": code ? code : "", - "short_desc": short_desc ? short_desc : "", - "long_desc": long_desc ? long_desc : "", - "images": image.{ - "url": url?url:"https://abc.com", - "size_type": size_type?size_type:"sm" - }[] - }, - "rating":$string(sc_retail_product.average_rating), - "rateable": true, - "price": $price(sc_retail_product), - "quantity": sc_retail_product.{ - "available": { - "count": stock_quantity ? stock_quantity : 0, - "measure":{ - "value": $string(stock_quantity ? stock_quantity : 0), - "unit": quantity_unit + "name": name ? name : "", + "code": code ? code : "", + "short_desc": short_desc ? short_desc : "", + "long_desc": long_desc ? long_desc : "", + "images": image.{ + "url": url?url:"https://abc.com", + "size_type": size_type?size_type:"sm" + }[] + }, + "rating":$string(sc_retail_product.average_rating), + "rateable": true, + "price": $price(sc_retail_product), + "quantity": sc_retail_product.{ + "available": { + "count": stock_quantity ? stock_quantity : 0, + "measure":{ + "value": $string(stock_quantity ? stock_quantity : 0), + "unit": quantity_unit + } + }, + "selected": { + "count": $itemQuantity($itemBody, %.id) } + }, + "category_ids": $map( + $filter(cat_attr_tag_relations, function($v) { $v.taxanomy = 'CATEGORY'}), + function($v) { + $string($v.taxanomy_id.id) + } + )[], + "fulfillment_ids": $count(item_fulfillment_ids) > 0 ? + $map(item_fulfillment_ids, function($v){ + $string($v.fulfilment_id.id) + })[] : + [], + "location_ids": $count(item_fulfillment_ids) > 0 ? + $map(item_fulfillment_ids, function($v){ + $string($v.location_id.id) + })[] : + [], + "tags": $tags(cat_attr_tag_relations), + "xinput": { + "form": { + "url": $xInput(%.%.context), + "mime_type": "text/html" + } + }, + "time": { + "duration": service.time_slot_per_client_in_min ? $string(service.time_slot_per_client_in_min) : "" } - }, - "category_ids": $map( - $filter(cat_attr_tag_relations, function($v) { $v.taxanomy = 'CATEGORY'}), - function($v) { - $string($v.taxanomy_id.id) - } - )[], - "fulfillment_ids": $count(item_fulfillment_ids) > 0 ? - $map(item_fulfillment_ids, function($v){ - $string($v.fulfilment_id.id) - })[] : - [], - "location_ids": $count(item_fulfillment_ids) > 0 ? - $map(item_fulfillment_ids, function($v){ - $string($v.location_id.id) - })[] : - [], - "tags": $tags(cat_attr_tag_relations), - "xinput": { - "form": { - "url": $xInput(%.%.context), - "mime_type": "text/html" + }[], + "quote": $quotePrice(message[0].items, message[0].itemsBody, context), + "billing": message.billing, + "categories": $map(message.category_ids, function($v) { + { + "id": $string($v.id), + "value": $v.value, + "createdAt": $v.createdAt, + "updatedAt": $v.updatedAt, + "publishedAt": $v.publishedAt, + "category_code": $v.category_code } - }, - "time": { - "duration": service.time_slot_per_client_in_min ? $string(service.time_slot_per_client_in_min) : "" - } - }[], - "quote": $quote(message.items), - "billing": message.billing, - "categories": $map(message.category_ids, function($v) { - { - "id": $string($v.id), - "value": $v.value, - "createdAt": $v.createdAt, - "updatedAt": $v.updatedAt, - "publishedAt": $v.publishedAt, - "category_code": $v.category_code - } - })[], - "fulfillments": $fulfillments(message.fulfillments, message.items), - "payments": $payments(message[0]), - "cancellation_terms": $cancellationTerms(message[0].items) + })[], + "fulfillments": $fulfillments(message.fulfillments, message.items), + "payments": $payments(message[0]), + "cancellation_terms": $cancellationTerms(message[0].items) + } } } -} +) diff --git a/plugins/beckn-bpp-adapter/server/mappings/on_select.jsonata b/plugins/beckn-bpp-adapter/server/mappings/on_select.jsonata index 783418c9..ba6918d4 100644 --- a/plugins/beckn-bpp-adapter/server/mappings/on_select.jsonata +++ b/plugins/beckn-bpp-adapter/server/mappings/on_select.jsonata @@ -1,4 +1,6 @@ -{ +( + $itemBody := message[0].itemsBody; + { "context": $context(context, $action), "message": { "order": { @@ -78,7 +80,7 @@ "duration": service.time_slot_per_client_in_min ? $string(service.time_slot_per_client_in_min) : "" } }[], - "quote": $quote(message.items), + "quote": $quotePrice(message[0].items, message[0].itemsBody, context), "categories": $map(message.category_ids, function($v) { { "id": $string($v.id), @@ -92,4 +94,5 @@ "fulfillments": $fulfillments(message.fulfillments, message.items) } } -} \ No newline at end of file +} +) \ No newline at end of file diff --git a/plugins/beckn-bpp-adapter/server/mappings/on_status.jsonata b/plugins/beckn-bpp-adapter/server/mappings/on_status.jsonata index d848e798..d40e51cb 100644 --- a/plugins/beckn-bpp-adapter/server/mappings/on_status.jsonata +++ b/plugins/beckn-bpp-adapter/server/mappings/on_status.jsonata @@ -1,10 +1,13 @@ + +( + $tags := message.order_id.tags; { "context": $context(context, $action), "message": { "order":{ "id": $string(message.order_id.id), "created_at": message.order_id.createdAt, - "provider": message.order_id.items.provider.{ + "provider": message.order_id.items[0].provider.{ "id": $string(id), "descriptor": { "name": provider_name, @@ -58,9 +61,10 @@ "unit": quantity_unit } }, - "selected":{ - "count":1 + "selected": { + "count": $itemQuantity($tags, %.id) } + }, "category_ids": $map( $filter(cat_attr_tag_relations, function($v) { $v.taxanomy = 'CATEGORY'}), @@ -181,9 +185,10 @@ "type": "PRE-ORDER", "transaction_id": transaction_id }[], - "quote": $quote(message.order_id.items), + "quote": $quotePrice(message[0].order_id.items, message[0].order_id.tags, context), "cancellation_terms": $cancellationTerms(message.order_id.items), "status": message.order_id.status } } -} \ No newline at end of file +} +) \ No newline at end of file diff --git a/plugins/beckn-bpp-adapter/server/services/confirm/commerce-workflow-service.ts b/plugins/beckn-bpp-adapter/server/services/confirm/commerce-workflow-service.ts index 25253f2d..e8f106db 100644 --- a/plugins/beckn-bpp-adapter/server/services/confirm/commerce-workflow-service.ts +++ b/plugins/beckn-bpp-adapter/server/services/confirm/commerce-workflow-service.ts @@ -448,7 +448,10 @@ export default ({ strapi }: { strapi: Strapi }) => ({ } }, order_id: orderId, - order_details: createOrder + order_details: createOrder, + // Attach itemsBody to get quantity of each items selected for order which will be used for dynamic price calculation + itemsBody: items.filter((i) => item.items.some((it) => String(it.id) === String(i.id))) + })); if (isEnergy(context)) { const { diff --git a/plugins/beckn-bpp-adapter/server/services/init/commerce-workflow-service.ts b/plugins/beckn-bpp-adapter/server/services/init/commerce-workflow-service.ts index eb05f4de..248aa19e 100644 --- a/plugins/beckn-bpp-adapter/server/services/init/commerce-workflow-service.ts +++ b/plugins/beckn-bpp-adapter/server/services/init/commerce-workflow-service.ts @@ -166,7 +166,10 @@ export default ({ strapi }: { strapi: Strapi }) => ({ }); } initDetails = initDetails.map((provider) => { - provider.items = provider.items.map((responseItem) => { + const providerItemIds = provider?.items.map((item) => String(item.id)); + // Filter items that exist in provider.items + const itemsBody = items.filter((item) => providerItemIds.includes(String(item.id))); + provider.items = provider?.items.map((responseItem) => { const bodyItem = items.find( (item) => Number(item.id) === responseItem.id ); @@ -182,7 +185,7 @@ export default ({ strapi }: { strapi: Strapi }) => ({ } return responseItem; }); - + provider.itemsBody = itemsBody; return provider; }); return initDetails; diff --git a/plugins/beckn-bpp-adapter/server/services/select/commerce-workflow-service.ts b/plugins/beckn-bpp-adapter/server/services/select/commerce-workflow-service.ts index 559c0a23..c8052727 100644 --- a/plugins/beckn-bpp-adapter/server/services/select/commerce-workflow-service.ts +++ b/plugins/beckn-bpp-adapter/server/services/select/commerce-workflow-service.ts @@ -138,6 +138,11 @@ export default ({ strapi }: { strapi: Strapi }) => ({ itemDetails[0] = { ...itemDetails[0], ...quantitySelected }; itemDetails = itemDetails.map((provider) => { + const providerItemIds = provider?.items.map((item) => String(item.id)); + // Filter items that exist in provider.items + const itemsBody = items.filter((item) => + providerItemIds.includes(String(item.id)) + ); provider.items = provider.items.map((responseItem) => { // Find the matching item in request body by id const bodyItem = items.find( @@ -156,7 +161,7 @@ export default ({ strapi }: { strapi: Strapi }) => ({ } return responseItem; }); - + provider.itemsBody = itemsBody; return provider; }); return itemDetails; diff --git a/plugins/beckn-bpp-adapter/server/tl/tl.helper.ts b/plugins/beckn-bpp-adapter/server/tl/tl.helper.ts index 3c4da88a..0cc56aed 100644 --- a/plugins/beckn-bpp-adapter/server/tl/tl.helper.ts +++ b/plugins/beckn-bpp-adapter/server/tl/tl.helper.ts @@ -88,6 +88,100 @@ export const quote = async (items: KeyValuePair[]) => { }; }; +// Domain-specific base price name mappings +const domainBasePriceNames = { + "Retail": "Item Price", + "dhp:pharmacy:0.1.0": "Item Price", + "supply-chain-services:assembly": "Base Price", + "mobility:1.1.0": "Base Fare", + "tourism": "Sub Total", + "dsep:courses": "Course Fee", + "hospitality": "Room Tariff", + "retail:1.1.0": "Item Price", + "uei:p2p_trading": "P2P Energy Cost", + "uei:charging": "Cost of Charge" +}; + +export const quotePrice = async ( + items: KeyValuePair[], + itemSelected: KeyValuePair[], + context: KeyValuePair +) => { + const breakup: KeyValuePair[] = []; + console.log("Context===>", context); + console.log("Items===>", items); + console.log("ItemSelected===>", itemSelected); + + let totalPriceValue = 0; + + items?.map((item) => { + const scProduct = item?.sc_retail_product; + + // Find the selected quantity for the item from itemSelected + const matchingItem = itemSelected.find( + (tag) => String(tag.id) === String(item.id) + ); + const selectedQuantity = matchingItem?.quantity?.selected?.count ?? 1; // Default to 1 if not found + + // Get domain-specific base price name or use default + const domain = context?.domain; + const basePriceName = domainBasePriceNames[domain]; + + // Process price breakup items if they exist + if (scProduct?.price_bareakup_ids?.length > 0) { + // Add all price breakups + scProduct.price_bareakup_ids.map((price_bareakup_id: KeyValuePair) => { + // Calculate price based on is_item_qty_dependent flag + const baseValue = Number(price_bareakup_id.value ?? 0); + const adjustedValue = price_bareakup_id.is_item_qty_dependent + ? baseValue * selectedQuantity + : baseValue; + + breakup.push({ + title: price_bareakup_id.title, + price: { + currency: price_bareakup_id.currency, + value: adjustedValue.toString(), + }, + item: { id: `${item.id || ""}` }, + }); + }); + + // If base price exists for this domain, add it to breakups + if (basePriceName && scProduct?.base_fee) { + breakup.push({ + title: basePriceName, + price: { + currency: scProduct.currency, + value: (Number(scProduct.base_fee) * selectedQuantity).toString(), + }, + item: { id: `${item.id || ""}` }, + }); + } + } else { + // If no breakups exist, just add base_fee * quantity to total price + if (scProduct?.base_fee) { + totalPriceValue += Number(scProduct.base_fee) * selectedQuantity; + } + } + }); + + // Calculate total priceValue as sum of all breakup.price.value plus any base_fee values + const breakupPriceValue = breakup.reduce( + (accumulator, currentValue) => + accumulator + Number(currentValue?.price?.value), + 0 + ); + + return { + price: { + value: (breakupPriceValue + totalPriceValue).toString(), + currency: items?.[0]?.sc_retail_product?.currency, + }, + breakup, + }; +}; + export const payments = async ( provider: KeyValuePair, incomingPrice: KeyValuePair, @@ -360,3 +454,12 @@ export const providerTags = (tagRelations) => { return Array.from(groupedRelationsMap.values()); }; + +export const itemQuantity = (tags: any, itemId: any) => { + if (!Array.isArray(tags) || tags.length === 0) { + return 1; + } + // Find the tag where id matches itemId + const matchingTag = tags.find((tag) => String(tag.id) === String(itemId)); + return matchingTag?.quantity?.selected?.count || 1; +}; diff --git a/plugins/beckn-bpp-adapter/server/util/calculations.util.ts b/plugins/beckn-bpp-adapter/server/util/calculations.util.ts new file mode 100644 index 00000000..a215a1be --- /dev/null +++ b/plugins/beckn-bpp-adapter/server/util/calculations.util.ts @@ -0,0 +1,28 @@ +export class CalculationsUtil { + static calculateOrderAmount(items: any, itemSelected: any) { + let totalAmount = 0; + + items?.forEach((item: any) => { + const scProduct = item?.sc_retail_product; + + // Find the selected quantity for the item from itemSelected + const matchingTag = itemSelected.find((tag: any) => String(tag.id) === String(item.id)); + const selectedQuantity = matchingTag?.quantity?.selected?.count ?? 1; + + if (scProduct?.base_fee) { + totalAmount += Number(scProduct.base_fee) * selectedQuantity; + } + + scProduct?.price_bareakup_ids?.forEach((price_bareakup_id: any) => { + const baseValue = Number(price_bareakup_id.value ?? 0); + const adjustedValue = price_bareakup_id.is_item_qty_dependent + ? baseValue * selectedQuantity + : baseValue; + + totalAmount += adjustedValue; + }); + }); + return totalAmount; + } + +} diff --git a/plugins/beckn-bpp-adapter/server/util/index.ts b/plugins/beckn-bpp-adapter/server/util/index.ts index df5e5a8b..ac2f028f 100644 --- a/plugins/beckn-bpp-adapter/server/util/index.ts +++ b/plugins/beckn-bpp-adapter/server/util/index.ts @@ -4,4 +4,5 @@ export * from './domain.util'; export * from './location.util'; export * from './search.util'; export * from './init.util'; -export * from './trade.util'; \ No newline at end of file +export * from './trade.util'; +export * from './calculations.util'; \ No newline at end of file