# Event Material
## Merchandise Types
### Consumable /Consumable Kit Merchandises
#### Consumable Merchandise (DO_OR1ND000001926125)
is a product has limitted stocked quantity in the WMS. When ordering Consumable Merchandises, need to check the available Quantity in WMS before allowing user to add to cart/checkout
Example: Pencil, Shirt,...
#### Consumable Kit Merchandise (DO_OR9ND000000074697)
is a virtual concept in Cortex (not available on WMS), A Consumable Kit Merchandise contains many Consumable Merchandises, handle by the Relation OR9ND000000074812. When ordering Consumable Kit Merchandise, Cortex will send to WMS an order contains Kit contents not the kit itself
```mermaid
flowchart LR
Kit--->|OR9ND000000074812|Consumable1 & Consumable2 & Consumable3
```
Example above kit, when user ordering N kit, Cortex will send to WMS an orders with 3 lines for N Consumable1, N Consumable2, N Consumable3
### Calendared/Calendared KitMerchandises
#### Calendared Merchandise (DO_OR1ND000001926061)
is a product has limitted stocked quantity in the WMS for renting. When user rent a Calendared Merchandise, they need to return the product back to WMS after renting period, otherwise, late fee will be charged.
Example: Table, Backdrop, ...
#### Calendared Kit Merchandise (DO_OR9ND000000066099)
Same logic as [**Consumable Kit Merchandise**](https://hackmd.io/cwLcdAiWT--ODpVZQ0jsMA#Consumable-Kit-Merchandise) but contains Calendared Merchandises and different relation
```mermaid
flowchart LR
Kit--->|OR9ND000000066168|KitHardware1 & KitHardware2 & KitHardware3
```
### Print On demand/ Print On demand Kit Merchandises
#### Print on demand Merchandise (DO_OR1ND000001926189)
is a product that user can order as many as they want. When ordering, Cortex just need to check if that product is available in the WMS regarless the quantity.
Example: Paper note, print documents
#### Print on demand Kit Merchandise (DO_X0AND000000020039)
is a virtual concept in Cortex (not available on WMS), A Print on Demand Kit Merchandise contains many Print on Demand Merchandises, A kit will content different quantity of Merchandise, handle by the Relation X0AND000000020163. When ordering Print on Demand Kit Merchandise, Cortex will send to WMS an order contains Kit contents not the kit itself with the quantity for each = order quantity * quantity in kit.The Quantity of each contain will be stored in Field LinkProperties of the DL_DOCLINKS entry
```mermaid
flowchart LR
Kit--->|X0AND000000020163 - x|KitContent1
Kit--->|X0AND000000020163 - y|KitContent2
Kit--->|X0AND000000020163 - z|KitContent3
```
Example: When user ordering N kit, Cortex will send to WMS an order
(N * x) KitContent1
(N * y) KitContent2
(N * z) KitContent3
### Kit to Stock (DO_OR9ND000000066135)
A kit contains multiple consumable products inside with different quantity, Diferrent with others Kit that only exist in Cortex, the Kit to stock is actually stored in WMS and user can order Kit to stock like a Comsumable Merchandise. When ordering Kit to stock, User will receive all the contents inside.
* There is a special function of Kit to stock call "Replenishment", Replenishment is used when we dont have any Available Quantity left for the Kit to stock but the Kit contents are available individually. In this case, admin can request a Replenishment order to group the individual kit contents to a new Kit To stock.
```mermaid
flowchart LR
KitToStock--->|OR9ND000000066474 - x|KitContent1
KitToStock--->|OR9ND000000066474 - y|KitContent2
KitToStock--->|OR9ND000000066474 - z|KitContent3
```
The available quantity of kit to stock will be calculated from all available kit contents
Example we have
```mermaid
flowchart LR
KitToStock--->|3|KitContent1
KitToStock--->|2|KitContent2
KitToStock--->|5|KitContent3
```
Quantity available on the WMS for each kit content are:
KitContent1: 7
KitContent2: 5
KitContent3: 9
So the quantity admin can do Replenishment is
Min(7/3, 5/2, 9/5) = 1 => only 1 Kit to stock can be replenishment
## WMS API
BaseURL: WarehouseManagementSystem_Tecsys_BO.Data.API.BaseURL
UserName: WarehouseManagementSystem_Tecsys_BO.Data.API.UserName
Password:WarehouseManagementSystem_Tecsys_BO.Data.API.Password
### Structure
```
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsc="wsclient.wms.tecsys.com">
<soapenv:Header/>
<soapenv:Body>
<wsc:search>
<arg0>
<userName>orange_webservice_bc</userName>
<password>***hidden field***</password>
[Main Content]
</arg0>
</wsc:search>
</soapenv:Body>
</soapenv:Envelope>
```
## Create product
### Cortex API to create Product
CreateOrUpdateProduct
Example: https://local.orangelogic.com/Core/API/WarehouseManagementSystem/v1.0/CreateOrUpdateProduct?TargetRecordID=XXXX
Create new incase [Product external field](https://hackmd.io/cwLcdAiWT--ODpVZQ0jsMA#Product-external-field) is empty, update if filled
### WMS Template SOAP API Create
Parameter: WarehouseManagementSystem_Tecsys_BO.Data.API.CreateOrUpdateProduct_Template_Create
End point: WmsStgPmF-Webservice-CreateBc
```
<transactions>
<action>Create</action>
<data>
<WmsStgPmF-Webservice-CreateBc>
<RecordId />
<Item>[ItemId]</Item>
<StagingAction>4</StagingAction>
<StagingRecordStatus>2</StagingRecordStatus>
<StagingMessageQueueId />
<Description1>[ItemId]</Description1>
<CustomChar1>[Title]</CustomChar1>
<CustomChar2>[LOBDisplayName]</CustomChar2>
<CustomChar3>[Representative]</CustomChar3>
<CustomChar4>[Representative]</CustomChar4>
<CustomChar5>[Support-Services.Product-category]</CustomChar5>
<CustomChar6>[Support-Services.Product-sub-category]</CustomChar6>
<Uom1>[Support-Services.Unit-of-measurement-(UOM1)]</Uom1>
<BreakUom>[Support-Services.Unit-of-measurement-(UOM1)]</BreakUom>
<InventoryClass>[InventoryClass]</InventoryClass>
<StockType>1</StockType>
<ShelfLife>99999</ShelfLife>
<AllowReceipt>Y</AllowReceipt>
<DefaultReleaseForPutaway>Y</DefaultReleaseForPutaway>
<TagTracking>N</TagTracking>
<DefaultUserCode1 />
<SerialNumberRequired>[SerialNumberRequired]</SerialNumberRequired>
<SerialNumberTrackingMethod>[SerialNumberTrackingMethod]</SerialNumberTrackingMethod>
<UseDefaultExpiryDate>N</UseDefaultExpiryDate>
<ExpiryLeadTime>0</ExpiryLeadTime>
</WmsStgPmF-Webservice-CreateBc>
</data>
</transactions>
```
### WMS Template SOAP API Update
Parameter: WarehouseManagementSystem_Tecsys_BO.Data.API.CreateOrUpdateProduct_Template_Update
End point: WmsStgPmF-Webservice-CreateBc
```
<transactions>
<action>Create</action>
<data>
<WmsStgPmF-Webservice-CreateBc>
<RecordId />
<Item>[ItemId]</Item>
<StagingAction>4</StagingAction>
<StagingRecordStatus>2</StagingRecordStatus>
<StagingMessageQueueId />
<Description1>[ItemId]</Description1>
<CustomChar1>[Title]</CustomChar1>
<CustomChar2>[LOBDisplayName]</CustomChar2>
<CustomChar3>[Representative]</CustomChar3>
<CustomChar4>[Representative]</CustomChar4>
<CustomChar5>[Support-Services.Product-category]</CustomChar5>
<CustomChar6>[Support-Services.Product-sub-category]</CustomChar6>
<Uom1>[Support-Services.Unit-of-measurement-(UOM1)]</Uom1>
<InventoryClass>[InventoryClass]</InventoryClass>
<StockType>1</StockType>
<ShelfLife>99999</ShelfLife>
<AllowReceipt>Y</AllowReceipt>
<DefaultReleaseForPutaway>Y</DefaultReleaseForPutaway>
<TagTracking>N</TagTracking>
<DefaultUserCode1 />
<SerialNumberRequired>[SerialNumberRequired]</SerialNumberRequired>
<SerialNumberTrackingMethod>[SerialNumberTrackingMethod]</SerialNumberTrackingMethod>
<UseDefaultExpiryDate>N</UseDefaultExpiryDate>
<ExpiryLeadTime>0</ExpiryLeadTime>
</WmsStgPmF-Webservice-CreateBc>
</data>
</transactions>
```
#### PlaceHolders
- [InventoryClass]: value based on serveral custom field on the product, check if fields below is true and take the first one
System.Merchandise.Basic.Kit-To-Stock => KitToStock
System.Merchandise.Basic.Calendared => Calendared
System.Merchandise.Basic.Manage-inventory => Consumable
Otherwise => On Demand
- [SerialNumberRequired]: Base on the InventoryClass, If "Calendared" => "Y", otherwise "N"
- [SerialNumberTrackingMethod]: Base on the InventoryClass, If "Calendared" => "2", otherwise "0"
- [Representative]: Using ```AssetLink_BO.CreateAssetLink_ForRepresentativeImage_NoSecurityChecks``` to create an shared link for the Representative_DO of the Merchandise
- [LOBDisplayName]: IA_IMAGEARCHIVE Display name of the IA_RecordID of DO or inherited
- Customfield placeholders: [Support-Services.Unit-of-measurement-(UOM1)], [Support-Services.Product-category], [Support-Services.Product-sub-category] Get from product using ObjectsInfoRetriver (need to be defined in WarehouseManagementSystem_Tecsys_BO.Data.Config.ProductMetadataCustomFields)
- Sql fields placeholders: Get from the sql WarehouseManagementSystem_Tecsys_BO.Data.API.CreateOrUpdateProduct_Fields
```
SELECT
ISNULL(NULLIF(ref(me.Data.Config.ProductIdOverrideFieldName),''),DO.Identifier) AS ItemId,
DO.Title As Title,
DO.CaptionLong As CaptionLong
FROM DO_DOCUMENTS DO
WHERE DO.RecordID = '[DO_RECORDID]' AND DO.Disabled = 0 AND ISNULL(DO.Binned,0) = 0
```
Example call:
```
<transactions>
<action>Create</action>
<data>
<WmsStgPmF-Webservice-CreateBc>
<RecordId />
<Item>BA936106</Item>
<StagingAction>4</StagingAction>
<StagingRecordStatus>2</StagingRecordStatus>
<StagingMessageQueueId />
<Description1>Test Product 2</Description1>
<CustomChar1>Cap Long 2</CustomChar1>
<CustomChar2></CustomChar2>
<CustomChar3>http://localhost/TES/AssetLink/3nhdxlwm5g7b0356u805mj0y3432l51e.jpg</CustomChar3>
<CustomChar4>http://localhost/TES/AssetLink/3nhdxlwm5g7b0356u805mj0y3432l51e.jpg</CustomChar4>
<InventoryClass>On Demand</InventoryClass>
<StockType>1</StockType>
<ShelfLife>99999</ShelfLife>
<AllowReceipt>Y</AllowReceipt>
<DefaultReleaseForPutaway>Y</DefaultReleaseForPutaway>
<TagTracking>N</TagTracking>
<DefaultUserCode1 />
<SerialNumberRequired>N</SerialNumberRequired>
<SerialNumberTrackingMethod>0</SerialNumberTrackingMethod>
<UseDefaultExpiryDate>N</UseDefaultExpiryDate>
<ExpiryLeadTime>0</ExpiryLeadTime>
</WmsStgPmF-Webservice-CreateBc>
</data>
</transactions>
```
### Product external field
When getting response from Creating API, Get the Element **ItemRecordId** and save to a field of Merchandise
Field to save defined at WarehouseManagementSystem_Tecsys_BO.Data.Mapping.Product_ExternalRecordID_Field
Value in COR: Id_Client_4
### Specific logs for Create/Update Product
| Case | Level | Content |
|---|---|---|
| Cannot parse Response | Error | $"Cannot parse results, XPATH: {oPM.GetDataStringTable(Const.DATA_API_PUSH_PRODUCT_RESULTS).Serialize()} in {sResponse}" |
|Save External Product Failed|Warn|$"Cannot save external RecordID {oResultData[Const.ELEMENT_ITEM_RECORD_ID]} for DO {_sDocumentRID}"|
|General Exception|Error|$"An unexpected error has occured when creating or updating product from WMS. Document {_sDocumentRID}"|
### BofA create product flow
#### Triggers
Call CreateOrUpdateProduct when "Record changed" on Merchandise
Condition
Status is **Approved** or **Active** or **Inactive** or **Obsolete**
## Ordering flow
### General Requirements
* **Cannot place an order with products from different catalogs**
* **Cannot place an order with different need-by date**
* The process is the following:
--> Select the product in the Product Catalog
--> Click on Order
- Select Date Needed
- Defaults with today’s date
- Must be in the future
- Enter Quantity Needed: should be less than Max replenish
- Return Date: (only for calendared product)
- The system check for inventory (only calendared/consumable product)
- If the product is out of stock, a message is displayed to the user "This product is currently out of stock. Please select different date needed/Return Dates or check with the line of business owner
--> Add to cart
--> Checkout
- Shipping Method default - can be modified (authority list)
- Billing Information:
- Cost center
- Defaults from the catalog, if null, then from the user
- Can be manually overridden
- Shipping Information
- Copy from billing if the same
- Enter shipping notes
--> Email Notification
- Email Confirmation to the requestor - Order is Placed
- Email Confirmation to the Line of Business - Order needs Approval
- Email Confirmation to the requestor - Order is Approved (if requires approval from the line of business)
- Email Confirmation to the requestor - Order is Shipped
### Details Implementation
#### Online Store workspace
Display all the active product and Available Quantity
##### Smart Field MerchandiseAvailableQuantity(Available Quantity (WMS))
#### Check available Quantity
WarehouseManagementSystem_Tecsys_BO.GetAvailableQuantityFromWMS
##### Get onhand quantity from WMS
* If requested product is a Kit, replace kit by its kit contents/hardwares before sending to WMS
* Get WMS product id from the field WarehouseManagementSystem_Tecsys_BO.Data.Config.ProductIdOverrideFieldName
* Send request to WMS by chunk, config at WarehouseManagementSystem_Tecsys_BO.Data.Config.GetOnhandQuantityChunkSize
* On Hand quantity Element name WarehouseManagementSystem_Tecsys_BO.Data.API.OnHandQuantityElementName (WmsPmF-OnHandQuantityEs on COR)
* WMS GetOnhandQuantity template WarehouseManagementSystem_Tecsys_BO.Data.API.GetOnhandQuantity_Template
```
<criteria>
<WmsPmF-OnHandQuantityEs>
<Item>[ItemID]</Item>
<Status>RDY</Status>
</WmsPmF-OnHandQuantityEs>
</criteria>
```
Example request
```
<criteria>
<WmsPmF-OnHandQuantityEs>
<Item>EOD-BAC-EDK-TTU-005;EOD-MRL-SGN-RB-001;EOD-MRL-TC-6-001</Item>
<Status>RDY</Status>
</WmsPmF-OnHandQuantityEs>
</criteria>
```
* Placeholders:
[ItemID]: A semicolon string contains all the requested productIDs "ProductID1; ProductID2"
* Response from WMS
```
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:searchResponse xmlns:ns2="wsclient.wms.tecsys.com">
<return>
<status>
<code>0</code>
<description>Successful</description>
<timestamp>2022-08-04 02:17:44</timestamp>
</status>
<criteria>
<WmsPmF-OnHandQuantityEs>
<Item>EOD-BAC-EDK-TTU-005;EOD-MRL-SGN-RB-001;EOD-MRL-TC-6-001</Item>
<Status>RDY</Status>
</WmsPmF-OnHandQuantityEs>
</criteria>
<result>
<WmsPmF-OnHandQuantityEs>
<Warehouse>COLLINS</Warehouse>
<Item>EOD-BAC-EDK-TTU-005</Item>
<PackageCode />
<InventoryLink>COLLINS|EOD-BAC-EDK-TTU-005|</InventoryLink>
<ThumbnailImage>https://thevault.bankofamerica.com/AssetLink/eq715sv2b73d13m8alhb0g324j6i61a5.jpg</ThumbnailImage>
<TotOnHandQty>1.000</TotOnHandQty>
<OutboundOrdQty>4.000</OutboundOrdQty>
</WmsPmF-OnHandQuantityEs>
<WmsPmF-OnHandQuantityEs>
<Warehouse>COLLINS</Warehouse>
<Item>EOD-MRL-SGN-RB-001</Item>
<PackageCode />
<InventoryLink>COLLINS|EOD-MRL-SGN-RB-001|</InventoryLink>
<ThumbnailImage>https://thevault.bankofamerica.com/AssetLink/6rm4855q627oimk1wjq2854lddy1jlqp.jpg</ThumbnailImage>
<TotOnHandQty>4.000</TotOnHandQty>
<OutboundOrdQty>2.000</OutboundOrdQty>
</WmsPmF-OnHandQuantityEs>
<WmsPmF-OnHandQuantityEs>
<Warehouse>COLLINS</Warehouse>
<Item>EOD-MRL-TC-6-001</Item>
<PackageCode />
<InventoryLink>COLLINS|EOD-MRL-TC-6-001|</InventoryLink>
<ThumbnailImage>https://thevault.bankofamerica.com/AssetLink/c12m4wj32410km2t85xde020b30lf7l8.jpg</ThumbnailImage>
<TotOnHandQty>0.000</TotOnHandQty>
<OutboundOrdQty>5.000</OutboundOrdQty>
</WmsPmF-OnHandQuantityEs>
</result>
<viewName>wms_pm_f.on_hand_quantity_es</viewName>
</return>
</ns2:searchResponse>
</soap:Body>
</soap:Envelope>
```
- From the WMS response, take all the descendants of <result> the parse all the element to a StringTable. The output of GetOnHanQuantity is a Dictionary<String, StringTable>
- Key: DO_RecordID of merchandise (map from WMS result to id_client)
- Value: Key-Value of child element of </WmsPmF-OnHandQuantityEs> and Value
#### Specific checking available quantity on Cortex
After getting the Available Onhand Quantity from WMS, Cotex need to calculate the Available based on Subtype
##### Print On Demand Merchandise
If the result from WMS has requested Item ID of the Print On demand, It means the print on demand merchandise is available for any request (AvailableQuantity = decimal.MaxValue)
##### Print On Demand Kit
Available when all [kit contents](https://hackmd.io/cwLcdAiWT--ODpVZQ0jsMA?both#Print-On-Demand-Merchandise) are available. (AvailableQuantity = decimal.MaxValue)
##### Consumable Merchandise
- Total Quantity Field (TotOnHandQty): used to see how many products in WMS before user ordering, 0 if empty
WarehouseManagementSystem_Tecsys_BO.Data.XMLParser.Inventory_FieldAddingWhenCalculateAvailableQuantity
- Outbound Quantity Field (OutboundOrdQty): Used to see how many products from the TotOnHandQty were ordered and shipped, 0 if empty
WarehouseManagementSystem_Tecsys_BO.Data.XMLParser.Inventory_FieldSubtractingWhenCalculateAvailableQuantity
***Available Quantity = Total Quantity Field (TotOnHandQty) - Outbound Quantity Field (OutboundOrdQty)***
##### Consumable Kit Merchandise
***Consumable Kit Available Quantity = Min(Available Quantity of Kit contents)***
##### Calendared Merchandise
- Total Quantity Field (TotOnHandQty): used to see how many products in WMS before user ordering, 0 if empty
WarehouseManagementSystem_Tecsys_BO.Data.XMLParser.Calendared_FieldAddingWhenCalculateAvailableQuantity
- Checking Calendared mechandise requires StartTime and EndTime
###### Total out bound quantity is control by Cortex by accumulating all the orders of product within the Start/End time of current orders:
- Sql to get the Invoice has product: WarehouseManagementSystem_Tecsys_BO.Data.Queries.GetUnavailableQuantityCalendaredItems
```
SELECT
II.DO_RecordID,
IH.PreparationDeadline - ref(me.Data.Config.ShippingDayMargin) AS PreparationDeadline,
IH.ReturnDate + ref(me.Data.Config.LateReturnBufferDays) AS ReturnDate,
II.Quantity AS Quantity,
II.ShippingTrackingNumber AS ShippingTrackingNumber
FROM IH_INVOICEHEADER IH
INNER JOIN II_INVOICEITEMS II ON II.IH_ParentHeader = IH.RecordID AND II.Disabled = 0 AND II.DO_RecordID IN ([ITEMS_RID]) AND II.II_LateReturnFee IS NULL AND II.ApprovalStatus != 'Credited'
WHERE IH.Status = 'FINALIZED' AND IH.Disabled = 0 AND IH.wmssystem = 'Tecsys' AND ISNULL(IH.wmsstatus,'') != 'CAN'
AND DATEADD(day, ref(me.Data.Config.LateReturnBufferDays), IH.ReturnDate) >= GetDate()
ORDER BY IH.PreparationDeadline ASC
```
- Approximate Start Time for Old Order = IH.PreparationDeadline - ref(me.Data.Config.ShippingDayMargin)
- Approximate End Time (ReturnDate) for Old Order = IH.ReturnDate + ref(me.Data.Config.LateReturnBufferDays)
- Logic to accumulate the Available Quantity from Onhand quantity and old Orders:
* If Order was shipped and the Approximate End Time < Requested Start Date, It means product can be returned to WMS before the Requested start date so that WMS can reuse the product for Requested Orders => Total Onhand quantity needs to add in Quantity of shipped order (returnable quantity)
* For all not shipped orders, If order has Renting time (from Approximate StartDate to Approximate EndDate) is within the requested Period => Total Onhand quantity need to subtract Quantity of not shipped order (ordered quantity)
##### Calendared Kit Merchandise
- Total Quantity Field (TotOnHandQty): Min total onhand quantity of all Kit contents
- After getting the Total Quantity, merge the quantity of Kit to orders from Cortex **(Use the kit to check on Cortex, not Kit contents)** follow [Calendared logic](https://hackmd.io/cwLcdAiWT--ODpVZQ0jsMA?both#Total-out-bound-quantity-is-control-by-Cortex-by-accumulating-all-the-orders-of-product-within-the-StartEnd-time-of-current-orders)
##### Kit to Stock Merchandise
Available quantity of Kit to Stock is same as the Consumable Merchandise. When checking Available Quantity for Kit to Stock, Cortex check the Kit to stock ID, not its contents.
* Available Replenishment Quantity for Kit to Stock
- Get all the available quantity of kit contents then accumulate with the required quantity in the Kit to get the Maximum Kit that we can replenish
Example we have
```mermaid
flowchart LR
KitToStock--->|3|KitContent1
KitToStock--->|2|KitContent2
KitToStock--->|5|KitContent3
```
Quantity available on the WMS for each kit content are:
KitContent1: 7
KitContent2: 5
KitContent3: 9
So the quantity admin can do Replenishment is
Min(7/3, 5/2, 9/5) = 1 => only 1 Kit to stock can be replenishment
#### Aggregator To get available Quantity
MerchandiseQuantityAggregator used in AvailableQuantity_VForm to display available quantity in thumb/default view of merchandise
#### Available Quantity (WMS) Smart Field: MerchandiseAvailableQuantity
MerchandiseAvailableQuantitySmartFieldProvider_BO
used to display the availble Quantity in the Table view or Report