@final-commerce/command-frame 0.9.0 → 0.10.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CommonTypes.d.ts +36 -3
- package/dist/actions/add-product-fee/mock.d.ts +1 -1
- package/dist/actions/add-product-fee/mock.js +15 -9
- package/dist/actions/add-product-to-cart/mock.d.ts +23 -0
- package/dist/actions/add-product-to-cart/mock.js +321 -1
- package/dist/actions/add-product-to-cart/types.d.ts +28 -2
- package/dist/actions/adjust-inventory/mock.js +0 -20
- package/dist/actions/adjust-inventory/types.d.ts +0 -13
- package/dist/actions/adjust-inventory/types.js +0 -1
- package/dist/actions/get-composite-availability/action.d.ts +6 -0
- package/dist/actions/get-composite-availability/action.js +8 -0
- package/dist/actions/get-composite-availability/mock.d.ts +2 -0
- package/dist/actions/get-composite-availability/mock.js +45 -0
- package/dist/actions/get-composite-availability/types.d.ts +29 -0
- package/dist/actions/get-composite-availability/types.js +1 -0
- package/dist/actions/get-composite-price/action.d.ts +6 -0
- package/dist/actions/get-composite-price/action.js +8 -0
- package/dist/actions/get-composite-price/mock.d.ts +2 -0
- package/dist/actions/get-composite-price/mock.js +37 -0
- package/dist/actions/get-composite-price/types.d.ts +31 -0
- package/dist/actions/get-composite-price/types.js +1 -0
- package/dist/actions/get-current-cart/mock.d.ts +1 -1
- package/dist/actions/get-current-cart/mock.js +13 -3
- package/dist/actions/get-current-cart/types.d.ts +7 -1
- package/dist/actions/get-product-modifier-selections/mock.js +12 -0
- package/dist/actions/get-products/mock.js +7 -3
- package/dist/actions/get-products/types.d.ts +1 -1
- package/dist/actions/set-product-modifier-selections/mock.js +12 -0
- package/dist/actions/set-variant-unavailable/action.d.ts +2 -0
- package/dist/actions/set-variant-unavailable/action.js +4 -0
- package/dist/actions/set-variant-unavailable/mock.d.ts +2 -0
- package/dist/actions/set-variant-unavailable/mock.js +12 -0
- package/dist/actions/set-variant-unavailable/types.d.ts +17 -0
- package/dist/actions/set-variant-unavailable/types.js +1 -0
- package/dist/demo/database.d.ts +54 -16
- package/dist/demo/database.js +219 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.js +6 -2
- package/dist/projects/render/mocks.js +6 -2
- package/dist/projects/render/types.d.ts +4 -2
- package/dist/pubsub/topics/products/catalog-visibility-changed/types.d.ts +17 -0
- package/dist/pubsub/topics/products/catalog-visibility-changed/types.js +1 -0
- package/dist/pubsub/topics/products/composite-changed/types.d.ts +17 -0
- package/dist/pubsub/topics/products/composite-changed/types.js +1 -0
- package/dist/pubsub/topics/products/index.d.ts +2 -2
- package/dist/pubsub/topics/products/index.js +33 -18
- package/dist/pubsub/topics/products/inventory-changed/types.d.ts +18 -0
- package/dist/pubsub/topics/products/inventory-changed/types.js +1 -0
- package/dist/pubsub/topics/products/types.d.ts +16 -10
- package/dist/pubsub/topics/products/types.js +7 -4
- package/package.json +2 -2
- package/dist/actions/get-custom-stock-actions/action.d.ts +0 -5
- package/dist/actions/get-custom-stock-actions/action.js +0 -7
- package/dist/actions/get-custom-stock-actions/mock.d.ts +0 -2
- package/dist/actions/get-custom-stock-actions/mock.js +0 -11
- package/dist/actions/get-custom-stock-actions/types.d.ts +0 -12
- package/dist/actions/get-custom-stock-actions/types.js +0 -2
package/dist/demo/database.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Stores mock data that mimics the Render environment
|
|
4
4
|
*/
|
|
5
5
|
import { CFProductType, CFUserTypes, CurrencyCode, } from '../CommonTypes';
|
|
6
|
-
import { extendPrice, resolveUnit, toBase } from '@final-commerce/common';
|
|
6
|
+
import { cheapestCompositeItem, cheapestCompositePrice, extendPrice, resolveUnit, toBase, } from '@final-commerce/common';
|
|
7
7
|
import { BookingRatePeriod, BookingResourceKind, BookingType, ReservationStatus } from '@final-commerce/common';
|
|
8
8
|
export * from './mocks';
|
|
9
9
|
// Asset Imports - Using Remote URLs to avoid build complexity with asset copying
|
|
@@ -228,6 +228,7 @@ const createSimpleProduct = (id, name, price, image, categories, description) =>
|
|
|
228
228
|
maxPrice: price,
|
|
229
229
|
status: 'active',
|
|
230
230
|
productType: CFProductType.SIMPLE,
|
|
231
|
+
composite: null,
|
|
231
232
|
taxTable: 'tax_standard',
|
|
232
233
|
description,
|
|
233
234
|
images: [image],
|
|
@@ -241,6 +242,7 @@ const createSimpleProduct = (id, name, price, image, categories, description) =>
|
|
|
241
242
|
salePrice: 0,
|
|
242
243
|
isOnSale: false,
|
|
243
244
|
manageStock: true,
|
|
245
|
+
unavailable: false,
|
|
244
246
|
externalId: `ext_${id}_var`,
|
|
245
247
|
attributes: [],
|
|
246
248
|
inventory: createInventory(100),
|
|
@@ -263,6 +265,7 @@ const createVariableProduct = (id, name, basePrice, largePrice, image, categorie
|
|
|
263
265
|
maxPrice: largePrice,
|
|
264
266
|
status: 'active',
|
|
265
267
|
productType: CFProductType.VARIABLE,
|
|
268
|
+
composite: null,
|
|
266
269
|
taxTable: 'tax_standard',
|
|
267
270
|
description,
|
|
268
271
|
images: [image],
|
|
@@ -276,6 +279,7 @@ const createVariableProduct = (id, name, basePrice, largePrice, image, categorie
|
|
|
276
279
|
salePrice: 0,
|
|
277
280
|
isOnSale: false,
|
|
278
281
|
manageStock: true,
|
|
282
|
+
unavailable: false,
|
|
279
283
|
externalId: `ext_${id}_var_s`,
|
|
280
284
|
attributes: [{ name: 'Size', value: 'Small' }],
|
|
281
285
|
inventory: createInventory(50),
|
|
@@ -287,6 +291,7 @@ const createVariableProduct = (id, name, basePrice, largePrice, image, categorie
|
|
|
287
291
|
salePrice: 0,
|
|
288
292
|
isOnSale: false,
|
|
289
293
|
manageStock: true,
|
|
294
|
+
unavailable: false,
|
|
290
295
|
externalId: `ext_${id}_var_l`,
|
|
291
296
|
attributes: [{ name: 'Size', value: 'Large' }],
|
|
292
297
|
inventory: createInventory(30),
|
|
@@ -296,6 +301,8 @@ const createVariableProduct = (id, name, basePrice, largePrice, image, categorie
|
|
|
296
301
|
};
|
|
297
302
|
export const MOCK_PRODUCT_BASIL_ALMOND = createSimpleProduct('prod_basil_almond', 'Basil Almond Paste', 1200, basilAlmondImg, [MOCK_CATEGORY_PASTES, MOCK_CATEGORY_SPECIALTY, MOCK_CATEGORY_VEGAN], 'A rich blend of fresh basil and roasted almonds.');
|
|
298
303
|
export const MOCK_PRODUCT_BEER = createVariableProduct('prod_beer', 'Beer Paste', 1500, 2500, beerImg, [MOCK_CATEGORY_PASTES, MOCK_CATEGORY_SPECIALTY], 'Unique paste infused with dark lager.');
|
|
304
|
+
// Demo of `setVariantUnavailable`: the large jar is marked unavailable at the outlet, so `addProductToCart` refuses it.
|
|
305
|
+
MOCK_PRODUCT_BEER.variants[1].unavailable = true;
|
|
299
306
|
export const MOCK_PRODUCT_BEET = createSimpleProduct('prod_beet', 'Beet Paste', 1000, beetImg, [MOCK_CATEGORY_PASTES, MOCK_CATEGORY_BASIC, MOCK_CATEGORY_VEGAN], 'Earthy and sweet beet paste, perfect for salads.');
|
|
300
307
|
export const MOCK_PRODUCT_CARAMELIZED = createVariableProduct('prod_caramelized', 'Caramelized Paste', 1400, 2200, caramelizedImg, [MOCK_CATEGORY_PASTES, MOCK_CATEGORY_SPECIALTY, MOCK_CATEGORY_VEGAN], 'Slow-cooked caramelized onion paste.');
|
|
301
308
|
export const MOCK_PRODUCT_GARLIC_ONION = createVariableProduct('prod_garlic_onion', 'Garlic Onion Paste', 1100, 1800, garlicOnionImg, [MOCK_CATEGORY_PASTES, MOCK_CATEGORY_BASIC, MOCK_CATEGORY_VEGAN], 'Classic savory base for any dish.');
|
|
@@ -658,7 +665,106 @@ export const MOCK_PRODUCT_HAIRCUT = {
|
|
|
658
665
|
productType: CFProductType.BOOKING,
|
|
659
666
|
bookingRulesId: 'rule_salon_30',
|
|
660
667
|
};
|
|
668
|
+
// A composite (FT-83) as the host serves it: the merchant's price 27.00, items add only their upcharges (B31); each item
|
|
669
|
+
// keeps its own tax (`taxTable` empty). One item points at a deleted variant (unavailable), one is a whole category.
|
|
670
|
+
export const MOCK_PRODUCT_PASTE_TRIO = {
|
|
671
|
+
...createSimpleProduct('prod_paste_trio', 'Paste Trio', 2700, basilAlmondImg, [MOCK_CATEGORY_PASTES], 'Pick a paste and a spicy one.'),
|
|
672
|
+
productType: CFProductType.COMPOSITE,
|
|
673
|
+
taxTable: '',
|
|
674
|
+
// B43: a composite has no modifiers of its own; its items carry theirs.
|
|
675
|
+
modifiers: [],
|
|
676
|
+
composite: {
|
|
677
|
+
available: true,
|
|
678
|
+
basePrice: 2700,
|
|
679
|
+
fromPrice: 2800,
|
|
680
|
+
parts: [
|
|
681
|
+
{
|
|
682
|
+
_id: 'part_paste',
|
|
683
|
+
name: 'Paste',
|
|
684
|
+
required: true,
|
|
685
|
+
min: 1,
|
|
686
|
+
max: 1,
|
|
687
|
+
defaultPick: { itemId: 'item_basil', variantId: 'prod_basil_almond_var_main' },
|
|
688
|
+
items: [
|
|
689
|
+
{
|
|
690
|
+
_id: 'item_basil',
|
|
691
|
+
variantId: 'prod_basil_almond_var_main',
|
|
692
|
+
categoryId: null,
|
|
693
|
+
name: 'Basil Almond Paste',
|
|
694
|
+
extraCharge: 0,
|
|
695
|
+
quantity: 1,
|
|
696
|
+
cost: 0,
|
|
697
|
+
unavailable: null,
|
|
698
|
+
choices: [
|
|
699
|
+
{
|
|
700
|
+
productId: 'prod_basil_almond',
|
|
701
|
+
variantId: 'prod_basil_almond_var_main',
|
|
702
|
+
name: 'Basil Almond Paste',
|
|
703
|
+
attributes: [],
|
|
704
|
+
// The picked product's own modifiers (B28): taxed at its table, sent in the pick.
|
|
705
|
+
modifiers: [
|
|
706
|
+
{
|
|
707
|
+
_id: 'mod_texture',
|
|
708
|
+
name: 'Texture',
|
|
709
|
+
selectionType: 'single',
|
|
710
|
+
required: false,
|
|
711
|
+
sortOrder: 0,
|
|
712
|
+
choices: [
|
|
713
|
+
{ _id: 'choice_smooth', name: 'Smooth', price: 0, sortOrder: 0 },
|
|
714
|
+
{ _id: 'choice_chunky', name: 'Chunky', price: 50, sortOrder: 1 },
|
|
715
|
+
],
|
|
716
|
+
},
|
|
717
|
+
],
|
|
718
|
+
},
|
|
719
|
+
],
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
_id: 'item_gone',
|
|
723
|
+
variantId: 'prod_gone_var_main',
|
|
724
|
+
categoryId: null,
|
|
725
|
+
name: '',
|
|
726
|
+
extraCharge: 0,
|
|
727
|
+
quantity: 1,
|
|
728
|
+
cost: 0,
|
|
729
|
+
unavailable: 'deleted',
|
|
730
|
+
choices: [],
|
|
731
|
+
},
|
|
732
|
+
],
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
_id: 'part_spicy',
|
|
736
|
+
name: null,
|
|
737
|
+
required: true,
|
|
738
|
+
min: 1,
|
|
739
|
+
max: 2,
|
|
740
|
+
// "Pick up to 2": no preselected pick (B29 = D36) — the till shows how many are still needed.
|
|
741
|
+
defaultPick: null,
|
|
742
|
+
items: [
|
|
743
|
+
{
|
|
744
|
+
_id: 'item_spicy',
|
|
745
|
+
variantId: null,
|
|
746
|
+
categoryId: MOCK_CATEGORY_SPICY.id,
|
|
747
|
+
name: 'Spicy',
|
|
748
|
+
extraCharge: 100,
|
|
749
|
+
quantity: 1,
|
|
750
|
+
cost: 100,
|
|
751
|
+
unavailable: null,
|
|
752
|
+
choices: [
|
|
753
|
+
{
|
|
754
|
+
productId: 'prod_habanero',
|
|
755
|
+
variantId: 'prod_habanero_var_main',
|
|
756
|
+
name: 'Habanero Paste',
|
|
757
|
+
attributes: [],
|
|
758
|
+
},
|
|
759
|
+
],
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
},
|
|
763
|
+
],
|
|
764
|
+
},
|
|
765
|
+
};
|
|
661
766
|
export const MOCK_PRODUCTS = [
|
|
767
|
+
MOCK_PRODUCT_PASTE_TRIO,
|
|
662
768
|
MOCK_PRODUCT_BASIL_ALMOND,
|
|
663
769
|
MOCK_PRODUCT_BEER,
|
|
664
770
|
MOCK_PRODUCT_BEET,
|
|
@@ -676,6 +782,114 @@ export const MOCK_PRODUCTS = [
|
|
|
676
782
|
MOCK_PRODUCT_HAIRCUT,
|
|
677
783
|
];
|
|
678
784
|
export const MOCK_ORDERS = [MOCK_ORDER_1, MOCK_ORDER_2, MOCK_ORDER_3, MOCK_ORDER_4];
|
|
785
|
+
/** Nothing hidden by default; `setMockDatabase({ catalogVisibility })` hides products at an outlet. */
|
|
786
|
+
export const MOCK_CATALOG_VISIBILITY = [];
|
|
787
|
+
/**
|
|
788
|
+
* The host's cart stock check for composites (kaching `compositeStockRefusal`), over the demo shelves — a copy: what the
|
|
789
|
+
* cart takes of each component's variant (plain lines and components × their line quantity) against its shelf here.
|
|
790
|
+
* The demo has no pools or units, so it counts per variant; untracked and backordered variants are never short.
|
|
791
|
+
*/
|
|
792
|
+
export function mockCompositeStockRefusal(products) {
|
|
793
|
+
const components = products.flatMap((product) => product.components ?? []);
|
|
794
|
+
const demand = new Map();
|
|
795
|
+
for (const product of products) {
|
|
796
|
+
if (product.components)
|
|
797
|
+
for (const component of product.components)
|
|
798
|
+
demand.set(component.variantId, (demand.get(component.variantId) ?? 0) + component.quantity * product.quantity);
|
|
799
|
+
else
|
|
800
|
+
demand.set(product.variantId, (demand.get(product.variantId) ?? 0) + product.quantity);
|
|
801
|
+
}
|
|
802
|
+
for (const component of components) {
|
|
803
|
+
const variant = MOCK_PRODUCTS.flatMap((product) => product.variants).find((candidate) => candidate._id === component.variantId);
|
|
804
|
+
if (!variant?.manageStock || variant.allowBackorder)
|
|
805
|
+
continue;
|
|
806
|
+
const left = variant.inventory?.find((row) => row.outletId === MOCK_OUTLET.id)?.stock ?? 0;
|
|
807
|
+
if ((demand.get(component.variantId) ?? 0) > left)
|
|
808
|
+
return `${component.name}: only ${left} left`;
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
/** Product ids hidden at the demo's active outlet. */
|
|
813
|
+
export const mockHiddenProductIds = () => new Set(MOCK_CATALOG_VISIBILITY.filter((row) => row.outletId === MOCK_OUTLET.id).map((row) => row.productId));
|
|
814
|
+
/**
|
|
815
|
+
* The host's outlet rules for a composite (kaching `resolveComposite`), over the demo's static view — a copy, change both
|
|
816
|
+
* in one step: an item whose product is hidden here is left out, a category item keeps only products sold here (none →
|
|
817
|
+
* `'empty'`), an Optional part with nothing to pick is not offered, a required one with nothing makes it unavailable,
|
|
818
|
+
* and the D36 default and "from" price are read off what is left.
|
|
819
|
+
*/
|
|
820
|
+
/** B41: the seats a bookable demo product has free in the slot starting at `startAt` — as the host reads them. */
|
|
821
|
+
export function mockBookableWindow(productId, startAt) {
|
|
822
|
+
const at = new Date(startAt);
|
|
823
|
+
const slot = mockBookingAvailability(productId, at, new Date(+at + 86400000)).slots.find((candidate) => +new Date(candidate.startAt) === +at && candidate.canStart) ?? null;
|
|
824
|
+
return { free: slot?.free ?? 0, slot };
|
|
825
|
+
}
|
|
826
|
+
export const mockIsBookable = (productId) => MOCK_PRODUCTS.find((product) => product._id === productId)?.productType === CFProductType.BOOKING;
|
|
827
|
+
export function mockCompositeAtOutlet(composite, hidden = mockHiddenProductIds(),
|
|
828
|
+
/** B41: the window bookable items are judged in; absent = `by_date`. */
|
|
829
|
+
slot) {
|
|
830
|
+
// B41: a bookable item is judged by seats in the window, never by stock; with no window it waits for a date.
|
|
831
|
+
const bookable = (item) => !!item.variantId && item.choices.some((choice) => mockIsBookable(choice.productId));
|
|
832
|
+
const seatsFor = (item) => !slot
|
|
833
|
+
? 'by_date'
|
|
834
|
+
: mockBookableWindow(item.choices[0].productId, slot.startAt).free >= item.quantity
|
|
835
|
+
? null
|
|
836
|
+
: 'fully_booked';
|
|
837
|
+
const offered = (item) => !item.unavailable || item.unavailable === 'by_date';
|
|
838
|
+
// B35: a tracked variant without `quantity` on the demo shelf here (and no backorder) is sold out.
|
|
839
|
+
const soldOut = (variantId, quantity) => {
|
|
840
|
+
const variant = MOCK_PRODUCTS.flatMap((product) => product.variants).find((candidate) => candidate._id === variantId);
|
|
841
|
+
if (!variant?.manageStock || variant.allowBackorder)
|
|
842
|
+
return false;
|
|
843
|
+
return (variant.inventory?.find((row) => row.outletId === MOCK_OUTLET.id)?.stock ?? 0) < quantity;
|
|
844
|
+
};
|
|
845
|
+
// B36e: "N left" on a unit-sold tracked choice — the demo shelf already reads in the variant's own unit.
|
|
846
|
+
const withStockLeft = (choice) => {
|
|
847
|
+
const variant = MOCK_PRODUCTS.flatMap((product) => product.variants).find((candidate) => candidate._id === choice.variantId);
|
|
848
|
+
const onHand = variant?.inventory?.find((row) => row.outletId === MOCK_OUTLET.id)?.stock;
|
|
849
|
+
return variant?.unitId && variant.manageStock && typeof onHand === 'number'
|
|
850
|
+
? { ...choice, stockLeft: onHand }
|
|
851
|
+
: choice;
|
|
852
|
+
};
|
|
853
|
+
const upcharge = (item) => ({
|
|
854
|
+
extraCharge: item.extraCharge,
|
|
855
|
+
quantity: item.quantity,
|
|
856
|
+
available: offered(item),
|
|
857
|
+
});
|
|
858
|
+
const parts = composite.parts
|
|
859
|
+
.map((part) => {
|
|
860
|
+
const items = part.items.flatMap((item) => {
|
|
861
|
+
if (item.variantId) {
|
|
862
|
+
if (item.choices.some((choice) => hidden.has(choice.productId)))
|
|
863
|
+
return [];
|
|
864
|
+
if (!item.unavailable && bookable(item))
|
|
865
|
+
return [{ ...item, unavailable: seatsFor(item) }];
|
|
866
|
+
return item.unavailable || !soldOut(item.variantId, item.quantity)
|
|
867
|
+
? [{ ...item, choices: item.choices.map(withStockLeft) }]
|
|
868
|
+
: [{ ...item, unavailable: 'out_of_stock', choices: [] }];
|
|
869
|
+
}
|
|
870
|
+
const sold = item.choices.filter((choice) => !hidden.has(choice.productId));
|
|
871
|
+
const choices = sold.filter((choice) => !soldOut(choice.variantId, item.quantity));
|
|
872
|
+
const unavailable = item.unavailable ?? (!sold.length ? 'empty' : !choices.length ? 'out_of_stock' : null);
|
|
873
|
+
return [{ ...item, choices: unavailable ? [] : choices.map(withStockLeft), unavailable }];
|
|
874
|
+
});
|
|
875
|
+
const index = part.min === 1 && part.max === 1 ? cheapestCompositeItem(items.map(upcharge)) : undefined;
|
|
876
|
+
const cheapest = index === undefined ? undefined : items[index];
|
|
877
|
+
return {
|
|
878
|
+
...part,
|
|
879
|
+
items,
|
|
880
|
+
defaultPick: cheapest?.variantId ? { itemId: cheapest._id, variantId: cheapest.variantId } : null,
|
|
881
|
+
};
|
|
882
|
+
})
|
|
883
|
+
.filter((part) => part.min > 0 || part.items.some(offered));
|
|
884
|
+
const cheapest = cheapestCompositePrice(parts.map((part) => ({ min: part.min, items: part.items.map(upcharge) })));
|
|
885
|
+
return {
|
|
886
|
+
...composite,
|
|
887
|
+
available: composite.available && parts.every((part) => part.min === 0 || part.items.some(offered)),
|
|
888
|
+
needsDate: parts.some((part) => part.items.some(bookable)),
|
|
889
|
+
fromPrice: composite.basePrice === null || cheapest === undefined ? null : composite.basePrice + cheapest,
|
|
890
|
+
parts,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
679
893
|
export const MOCK_PARKED_ORDERS = [MOCK_PARKED_ORDER_1, MOCK_PARKED_ORDER_2];
|
|
680
894
|
// Compatibility Exports (reassigned by setMockDatabase)
|
|
681
895
|
export let MOCK_USER = MOCK_USERS[0];
|
|
@@ -760,6 +974,9 @@ export function setMockDatabase(config) {
|
|
|
760
974
|
if (config.bookings !== undefined) {
|
|
761
975
|
MOCK_BOOKINGS.splice(0, MOCK_BOOKINGS.length, ...config.bookings);
|
|
762
976
|
}
|
|
977
|
+
if (config.catalogVisibility !== undefined) {
|
|
978
|
+
MOCK_CATALOG_VISIBILITY.splice(0, MOCK_CATALOG_VISIBILITY.length, ...config.catalogVisibility);
|
|
979
|
+
}
|
|
763
980
|
if (MOCK_OUTLETS.length > 0) {
|
|
764
981
|
Object.assign(MOCK_OUTLET_MAIN, MOCK_OUTLETS[0]);
|
|
765
982
|
if (MOCK_OUTLET_MAIN.id === undefined && MOCK_OUTLET_MAIN._id !== undefined) {
|
|
@@ -1132,6 +1349,7 @@ export const buildModifierRows = (modifiers, lineQuantity) => {
|
|
|
1132
1349
|
amount: extendPrice(modifier.unitPrice * modifier.quantity, quantity),
|
|
1133
1350
|
tax: 0,
|
|
1134
1351
|
...(modifier.taxTableId ? { taxTableId: modifier.taxTableId } : {}),
|
|
1352
|
+
...(modifier.componentIndex !== undefined ? { componentIndex: modifier.componentIndex } : {}),
|
|
1135
1353
|
}));
|
|
1136
1354
|
return { rows, modifiersTotal: rows.reduce((sum, row) => sum + row.amount, 0) };
|
|
1137
1355
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -29,13 +29,15 @@ export declare const command: {
|
|
|
29
29
|
readonly addProductNote: import(".").AddProductNote;
|
|
30
30
|
readonly addProductFee: import(".").AddProductFee;
|
|
31
31
|
readonly getProductModifierSelections: import(".").GetProductModifierSelections;
|
|
32
|
+
readonly getCompositePrice: import(".").GetCompositePrice;
|
|
33
|
+
readonly getCompositeAvailability: import(".").GetCompositeAvailability;
|
|
32
34
|
readonly setProductModifierSelections: import(".").SetProductModifierSelections;
|
|
33
35
|
readonly setActiveProductFee: import(".").SetActiveProductFee;
|
|
34
36
|
readonly setActiveProductDiscount: import(".").SetActiveProductDiscount;
|
|
35
37
|
readonly getActiveProduct: import(".").GetActiveProduct;
|
|
36
38
|
readonly setActiveProduct: import(".").SetActiveProduct;
|
|
37
39
|
readonly adjustInventory: import(".").AdjustInventory;
|
|
38
|
-
readonly
|
|
40
|
+
readonly setVariantUnavailable: import(".").SetVariantUnavailable;
|
|
39
41
|
readonly addOrderNote: import(".").AddOrderNote;
|
|
40
42
|
readonly addCartFee: import(".").AddCartFee;
|
|
41
43
|
readonly getCurrentCart: import(".").GetCurrentCart;
|
|
@@ -175,13 +177,15 @@ export type { GetFinalContext, GetFinalContextResponse } from './actions/get-fin
|
|
|
175
177
|
export type { AddProductNote, AddProductNoteParams, AddProductNoteResponse } from './actions/add-product-note/types';
|
|
176
178
|
export type { AddProductFee, AddProductFeeParams, AddProductFeeResponse } from './actions/add-product-fee/types';
|
|
177
179
|
export type { GetProductModifierSelections, GetProductModifierSelectionsParams, GetProductModifierSelectionsResponse, ModifierSelection, ModifierChoiceSelection, } from './actions/get-product-modifier-selections/types';
|
|
180
|
+
export type { GetCompositePrice, GetCompositePriceParams, GetCompositePriceResponse, } from './actions/get-composite-price/types';
|
|
181
|
+
export type { GetCompositeAvailability, GetCompositeAvailabilityParams, GetCompositeAvailabilityResponse, CFCompositeSlot, } from './actions/get-composite-availability/types';
|
|
178
182
|
export type { SetProductModifierSelections, SetProductModifierSelectionsParams, SetProductModifierSelectionsResponse, } from './actions/set-product-modifier-selections/types';
|
|
179
183
|
export type { SetActiveProductFee, SetActiveProductFeeParams, SetActiveProductFeeResponse, } from './actions/set-active-product-fee/types';
|
|
180
184
|
export type { SetActiveProductDiscount, SetActiveProductDiscountParams, SetActiveProductDiscountResponse, } from './actions/set-active-product-discount/types';
|
|
181
185
|
export type { GetActiveProduct, GetActiveProductResponse } from './actions/get-active-product/types';
|
|
182
186
|
export type { SetActiveProduct, SetActiveProductParams, SetActiveProductResponse, } from './actions/set-active-product/types';
|
|
183
|
-
export type { AdjustInventory, AdjustInventoryParams, AdjustInventoryResponse
|
|
184
|
-
export type {
|
|
187
|
+
export type { AdjustInventory, AdjustInventoryParams, AdjustInventoryResponse } from './actions/adjust-inventory/types';
|
|
188
|
+
export type { SetVariantUnavailable, SetVariantUnavailableParams, SetVariantUnavailableResponse, } from './actions/set-variant-unavailable/types';
|
|
185
189
|
export type { AddOrderNote, AddOrderNoteParams, AddOrderNoteResponse } from './actions/add-order-note/types';
|
|
186
190
|
export type { AddCartFee, AddCartFeeParams, AddCartFeeResponse } from './actions/add-cart-fee/types';
|
|
187
191
|
export type { ClearCart, ClearCartResponse } from './actions/clear-cart/types';
|
package/dist/index.js
CHANGED
|
@@ -28,13 +28,15 @@ import { updateCartItemQuantity } from './actions/update-cart-item-quantity/acti
|
|
|
28
28
|
import { addProductNote } from './actions/add-product-note/action';
|
|
29
29
|
import { addProductFee } from './actions/add-product-fee/action';
|
|
30
30
|
import { getProductModifierSelections } from './actions/get-product-modifier-selections/action';
|
|
31
|
+
import { getCompositePrice } from './actions/get-composite-price/action';
|
|
32
|
+
import { getCompositeAvailability } from './actions/get-composite-availability/action';
|
|
31
33
|
import { setProductModifierSelections } from './actions/set-product-modifier-selections/action';
|
|
32
34
|
import { setActiveProductFee } from './actions/set-active-product-fee/action';
|
|
33
35
|
import { setActiveProductDiscount } from './actions/set-active-product-discount/action';
|
|
34
36
|
import { getActiveProduct } from './actions/get-active-product/action';
|
|
35
37
|
import { setActiveProduct } from './actions/set-active-product/action';
|
|
36
38
|
import { adjustInventory } from './actions/adjust-inventory/action';
|
|
37
|
-
import {
|
|
39
|
+
import { setVariantUnavailable } from './actions/set-variant-unavailable/action';
|
|
38
40
|
// Order Actions
|
|
39
41
|
import { addOrderNote } from './actions/add-order-note/action';
|
|
40
42
|
import { addCartFee } from './actions/add-cart-fee/action';
|
|
@@ -175,13 +177,15 @@ export const command = {
|
|
|
175
177
|
addProductNote,
|
|
176
178
|
addProductFee,
|
|
177
179
|
getProductModifierSelections,
|
|
180
|
+
getCompositePrice,
|
|
181
|
+
getCompositeAvailability,
|
|
178
182
|
setProductModifierSelections,
|
|
179
183
|
setActiveProductFee,
|
|
180
184
|
setActiveProductDiscount,
|
|
181
185
|
getActiveProduct,
|
|
182
186
|
setActiveProduct,
|
|
183
187
|
adjustInventory,
|
|
184
|
-
|
|
188
|
+
setVariantUnavailable,
|
|
185
189
|
// Order Actions
|
|
186
190
|
addOrderNote,
|
|
187
191
|
addCartFee,
|
|
@@ -13,6 +13,8 @@ import { mockRemoveOrderNote } from '../../actions/remove-order-note/mock';
|
|
|
13
13
|
import { mockAddProductDiscount } from '../../actions/add-product-discount/mock';
|
|
14
14
|
import { mockAddProductFee } from '../../actions/add-product-fee/mock';
|
|
15
15
|
import { mockGetProductModifierSelections } from '../../actions/get-product-modifier-selections/mock';
|
|
16
|
+
import { mockGetCompositePrice } from '../../actions/get-composite-price/mock';
|
|
17
|
+
import { mockGetCompositeAvailability } from '../../actions/get-composite-availability/mock';
|
|
16
18
|
import { mockSetProductModifierSelections } from '../../actions/set-product-modifier-selections/mock';
|
|
17
19
|
import { mockRemoveProductDiscount } from '../../actions/remove-product-discount/mock';
|
|
18
20
|
import { mockRemoveProductFee } from '../../actions/remove-product-fee/mock';
|
|
@@ -26,7 +28,7 @@ import { mockRemoveProductFromCart } from '../../actions/remove-product-from-car
|
|
|
26
28
|
import { mockRemoveCartDiscount } from '../../actions/remove-cart-discount/mock';
|
|
27
29
|
import { mockUpdateCartItemQuantity } from '../../actions/update-cart-item-quantity/mock';
|
|
28
30
|
import { mockAdjustInventory } from '../../actions/adjust-inventory/mock';
|
|
29
|
-
import {
|
|
31
|
+
import { mockSetVariantUnavailable } from '../../actions/set-variant-unavailable/mock';
|
|
30
32
|
import { mockAssignCustomer } from '../../actions/assign-customer/mock';
|
|
31
33
|
import { mockAuthenticateUser } from '../../actions/authenticate-user/mock';
|
|
32
34
|
import { mockCalculateRefundTotal } from '../../actions/calculate-refund-total/mock';
|
|
@@ -123,6 +125,8 @@ export const RENDER_MOCKS = {
|
|
|
123
125
|
addProductDiscount: mockAddProductDiscount,
|
|
124
126
|
addProductFee: mockAddProductFee,
|
|
125
127
|
getProductModifierSelections: mockGetProductModifierSelections,
|
|
128
|
+
getCompositePrice: mockGetCompositePrice,
|
|
129
|
+
getCompositeAvailability: mockGetCompositeAvailability,
|
|
126
130
|
setProductModifierSelections: mockSetProductModifierSelections,
|
|
127
131
|
setActiveProductFee: mockSetActiveProductFee,
|
|
128
132
|
setActiveProductDiscount: mockSetActiveProductDiscount,
|
|
@@ -133,7 +137,7 @@ export const RENDER_MOCKS = {
|
|
|
133
137
|
removeProductFromCart: mockRemoveProductFromCart,
|
|
134
138
|
updateCartItemQuantity: mockUpdateCartItemQuantity,
|
|
135
139
|
adjustInventory: mockAdjustInventory,
|
|
136
|
-
|
|
140
|
+
setVariantUnavailable: mockSetVariantUnavailable,
|
|
137
141
|
assignCustomer: mockAssignCustomer,
|
|
138
142
|
authenticateUser: mockAuthenticateUser,
|
|
139
143
|
calculateRefundTotal: mockCalculateRefundTotal,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExampleFunction, GetProducts, AddCustomSale, EditCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables,
|
|
1
|
+
import type { ExampleFunction, GetProducts, AddCustomSale, EditCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, GetProductModifierSelections, GetCompositePrice, GetCompositeAvailability, SetProductModifierSelections, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, SetVariantUnavailable, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, VoidOrder, InitiateRefund, CashPayment, GetCashRoundingAmount, TapToPayPayment, TerminalPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, RedeemRefund, GetRefundPlan, CheckPermission, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, GetActiveStation, GetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem, CanTransition, GetAvailableTransitions, ApplyTransition, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout, SendEmail, SendSms, GetBookingResources, GetBookingAvailability, GetBookings, HoldBooking, AddBookingToCart, RemoveBookingFromCart, CancelBooking, CreatePaymentLink, ChargeMoto, GetTimeClockStatus } from '../../index';
|
|
2
2
|
export interface RenderProviderActions {
|
|
3
3
|
exampleFunction: ExampleFunction;
|
|
4
4
|
getProducts: GetProducts;
|
|
@@ -29,13 +29,15 @@ export interface RenderProviderActions {
|
|
|
29
29
|
addProductNote: AddProductNote;
|
|
30
30
|
addProductFee: AddProductFee;
|
|
31
31
|
getProductModifierSelections: GetProductModifierSelections;
|
|
32
|
+
getCompositePrice: GetCompositePrice;
|
|
33
|
+
getCompositeAvailability: GetCompositeAvailability;
|
|
32
34
|
setProductModifierSelections: SetProductModifierSelections;
|
|
33
35
|
setActiveProductFee: SetActiveProductFee;
|
|
34
36
|
setActiveProductDiscount: SetActiveProductDiscount;
|
|
35
37
|
getActiveProduct: GetActiveProduct;
|
|
36
38
|
setActiveProduct: SetActiveProduct;
|
|
37
39
|
adjustInventory: AdjustInventory;
|
|
38
|
-
|
|
40
|
+
setVariantUnavailable: SetVariantUnavailable;
|
|
39
41
|
addOrderNote: AddOrderNote;
|
|
40
42
|
addCartFee: AddCartFee;
|
|
41
43
|
clearCart: ClearCart;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TopicEvent } from '../../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Payload for catalog-visibility-changed: a product was hidden or shown at an outlet. `getProducts` leaves hidden
|
|
4
|
+
* products out and a composite offers only items sold at the till's outlet — debounce, then re-ask `getProducts`.
|
|
5
|
+
*/
|
|
6
|
+
export interface CatalogVisibilityChangedPayload {
|
|
7
|
+
visibility: {
|
|
8
|
+
_id: string;
|
|
9
|
+
productId: string;
|
|
10
|
+
outletId: string;
|
|
11
|
+
isDeleted?: boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Typed event for catalog-visibility-changed
|
|
16
|
+
*/
|
|
17
|
+
export type CatalogVisibilityChangedEvent = TopicEvent<CatalogVisibilityChangedPayload>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TopicEvent } from '../../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Payload for composite-changed (FT-83): a part or item of a composite arrived. A save replaces all of a composite's
|
|
4
|
+
* parts and items at once (old rows come as `isDeleted: true`, new ones with new ids), so one save fires this many
|
|
5
|
+
* times — debounce, then re-ask `getProducts`; never patch `product.composite` from the row.
|
|
6
|
+
*/
|
|
7
|
+
export interface CompositeChangedPayload {
|
|
8
|
+
compositeRow: {
|
|
9
|
+
_id: string;
|
|
10
|
+
compositeProductId: string;
|
|
11
|
+
isDeleted?: boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Typed event for composite-changed
|
|
16
|
+
*/
|
|
17
|
+
export type CompositeChangedEvent = TopicEvent<CompositeChangedPayload>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Products Topic Definition
|
|
3
3
|
* Defines the products topic and its available event types
|
|
4
4
|
*/
|
|
5
|
-
import type { TopicDefinition } from
|
|
5
|
+
import type { TopicDefinition } from '../../types';
|
|
6
6
|
export declare const productsTopic: TopicDefinition;
|
|
7
|
-
export * from
|
|
7
|
+
export * from './types';
|
|
@@ -3,31 +3,46 @@
|
|
|
3
3
|
* Defines the products topic and its available event types
|
|
4
4
|
*/
|
|
5
5
|
export const productsTopic = {
|
|
6
|
-
id:
|
|
7
|
-
name:
|
|
8
|
-
description:
|
|
6
|
+
id: 'products',
|
|
7
|
+
name: 'Products',
|
|
8
|
+
description: 'Topic for product-related events',
|
|
9
9
|
eventTypes: [
|
|
10
10
|
{
|
|
11
|
-
id:
|
|
12
|
-
name:
|
|
13
|
-
description:
|
|
11
|
+
id: 'product-created',
|
|
12
|
+
name: 'Product Created',
|
|
13
|
+
description: 'Published when a new product is synced/created',
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
|
-
id:
|
|
17
|
-
name:
|
|
18
|
-
description:
|
|
16
|
+
id: 'product-updated',
|
|
17
|
+
name: 'Product Updated',
|
|
18
|
+
description: 'Published when a product is synced/updated',
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
|
-
id:
|
|
22
|
-
name:
|
|
23
|
-
description: "Published when a
|
|
21
|
+
id: 'composite-changed',
|
|
22
|
+
name: 'Composite Changed',
|
|
23
|
+
description: "Published when a composite's part or item is synced; re-ask getProducts",
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
id:
|
|
27
|
-
name:
|
|
28
|
-
description:
|
|
29
|
-
}
|
|
30
|
-
|
|
26
|
+
id: 'catalog-visibility-changed',
|
|
27
|
+
name: 'Catalog Visibility Changed',
|
|
28
|
+
description: 'Published when a product is hidden or shown at an outlet; re-ask getProducts',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'inventory-changed',
|
|
32
|
+
name: 'Inventory Changed',
|
|
33
|
+
description: "Published when a variant's stock at an outlet syncs; re-ask getProducts",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'set-active-product',
|
|
37
|
+
name: 'Set Active Product',
|
|
38
|
+
description: 'Published when a product is set as the active product',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'get-active-product',
|
|
42
|
+
name: 'Get Active Product',
|
|
43
|
+
description: 'Published when a product is retrieved as the active product',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
31
46
|
};
|
|
32
47
|
// Re-export types
|
|
33
|
-
export * from
|
|
48
|
+
export * from './types';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TopicEvent } from '../../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Payload for inventory-changed (B35): a variant's stock at an outlet moved. A composite may turn Sold out or
|
|
4
|
+
* Unavailable by its components' shelves — debounce, then re-ask `getProducts`.
|
|
5
|
+
*/
|
|
6
|
+
export interface InventoryChangedPayload {
|
|
7
|
+
inventory: {
|
|
8
|
+
_id: string;
|
|
9
|
+
variantId: string;
|
|
10
|
+
outletId: string;
|
|
11
|
+
quantity?: number | null;
|
|
12
|
+
manageStock?: boolean;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Typed event for inventory-changed
|
|
17
|
+
*/
|
|
18
|
+
export type InventoryChangedEvent = TopicEvent<InventoryChangedPayload>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
* Products Topic Types
|
|
3
3
|
* Aggregated types for all product-related events
|
|
4
4
|
*/
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
export * from './product-created/types';
|
|
6
|
+
export * from './product-updated/types';
|
|
7
|
+
export * from './composite-changed/types';
|
|
8
|
+
export * from './catalog-visibility-changed/types';
|
|
9
|
+
export * from './inventory-changed/types';
|
|
10
|
+
export * from './product-set-active/types';
|
|
11
|
+
export * from './product-get-active/types';
|
|
12
|
+
import type { ProductCreatedPayload } from './product-created/types';
|
|
13
|
+
import type { ProductUpdatedPayload } from './product-updated/types';
|
|
14
|
+
import type { CompositeChangedPayload } from './composite-changed/types';
|
|
15
|
+
import type { CatalogVisibilityChangedPayload } from './catalog-visibility-changed/types';
|
|
16
|
+
import type { InventoryChangedPayload } from './inventory-changed/types';
|
|
17
|
+
import type { ProductSetActivePayload } from './product-set-active/types';
|
|
18
|
+
import type { ProductGetActivePayload } from './product-get-active/types';
|
|
19
|
+
export type ProductsEventPayload = ProductCreatedPayload | ProductUpdatedPayload | CompositeChangedPayload | CatalogVisibilityChangedPayload | InventoryChangedPayload | ProductSetActivePayload | ProductGetActivePayload;
|
|
20
|
+
export type ProductsEventType = 'product-created' | 'product-updated' | 'composite-changed' | 'catalog-visibility-changed' | 'inventory-changed' | 'set-active-product' | 'get-active-product';
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* Aggregated types for all product-related events
|
|
4
4
|
*/
|
|
5
5
|
// Re-export all event types
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
6
|
+
export * from './product-created/types';
|
|
7
|
+
export * from './product-updated/types';
|
|
8
|
+
export * from './composite-changed/types';
|
|
9
|
+
export * from './catalog-visibility-changed/types';
|
|
10
|
+
export * from './inventory-changed/types';
|
|
11
|
+
export * from './product-set-active/types';
|
|
12
|
+
export * from './product-get-active/types';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@final-commerce/command-frame",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-staging.2",
|
|
4
4
|
"description": "Commands Frame library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"ignoreBranchesMissingTickets": true
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@final-commerce/common": "2.
|
|
60
|
+
"@final-commerce/common": "2.6.0-staging.1"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@commitlint/cli": "^19.0.0",
|