From d43f29c1531d62f4a43f645ff0d1bf2f045c8f1b Mon Sep 17 00:00:00 2001 From: MohamedAliSmk Date: Tue, 18 Aug 2026 14:06:24 +0300 Subject: [PATCH 1/8] Add Magento registry, boot-gated Vue adapters, and desk JS yield on develop. Keep origin/develop baseline offers; GWP/auth Python stay in posnext_promotions. Free-bundle SI merge is no longer in the POS Next invoice class. Co-authored-by: Cursor --- POS/src/App.vue | 2 + .../components/common/AuthorizationDialog.vue | 185 ++++++++++ POS/src/components/sale/CouponDialog.vue | 5 +- POS/src/components/sale/CouponManagement.vue | 13 +- .../components/sale/CreateCustomerDialog.vue | 233 ++++++++++--- POS/src/components/sale/EditItemDialog.vue | 209 +++++++++++- POS/src/components/sale/InvoiceCart.vue | 319 ++++++++++++++---- POS/src/components/sale/PaymentDialog.vue | 56 +++ .../components/sale/PromotionManagement.vue | 17 +- .../components/sale/ReturnInvoiceDialog.vue | 44 ++- POS/src/composables/useAuthorization.js | 111 ++++++ POS/src/composables/useInvoice.js | 3 +- POS/src/stores/__tests__/posOffers.spec.js | 70 ++++ POS/src/stores/bootstrap.js | 16 + POS/src/stores/posOffers.js | 5 +- POS/src/stores/posSettings.js | 8 + POS/src/utils/offerSchedule.js | 104 ++++++ POS/src/utils/promoApi.js | 122 +++++++ pos_next/api/bootstrap.py | 6 + pos_next/api/constants.py | 12 + pos_next/api/customers.py | 35 +- pos_next/api/pos_profile.py | 4 + pos_next/api/sales_invoice_hooks.py | 34 +- pos_next/api/wallet.py | 84 ++++- pos_next/hooks.py | 9 + pos_next/integrations/__init__.py | 1 + pos_next/integrations/registry.py | 67 ++++ pos_next/overrides/sales_invoice.py | 272 +++++++-------- .../doctype/pos_settings/pos_settings.py | 4 + pos_next/public/js/pricing_rule.js | 58 ++-- pos_next/public/js/promotional_scheme.js | 73 ++-- pos_next/test_no_promotions_import.py | 60 ++++ pos_next/test_split_smoke.py | 85 +++++ scripts/run_split_smoke_tests.py | 90 +++++ 34 files changed, 2080 insertions(+), 336 deletions(-) create mode 100644 POS/src/components/common/AuthorizationDialog.vue create mode 100644 POS/src/composables/useAuthorization.js create mode 100644 POS/src/stores/__tests__/posOffers.spec.js create mode 100644 POS/src/utils/offerSchedule.js create mode 100644 POS/src/utils/promoApi.js create mode 100644 pos_next/integrations/__init__.py create mode 100644 pos_next/integrations/registry.py create mode 100644 pos_next/test_no_promotions_import.py create mode 100644 pos_next/test_split_smoke.py create mode 100644 scripts/run_split_smoke_tests.py diff --git a/POS/src/App.vue b/POS/src/App.vue index 73c5f397b..f108f0cf6 100644 --- a/POS/src/App.vue +++ b/POS/src/App.vue @@ -2,10 +2,12 @@
+
diff --git a/POS/src/components/common/AuthorizationDialog.vue b/POS/src/components/common/AuthorizationDialog.vue new file mode 100644 index 000000000..b57032a69 --- /dev/null +++ b/POS/src/components/common/AuthorizationDialog.vue @@ -0,0 +1,185 @@ + + + diff --git a/POS/src/components/sale/CouponDialog.vue b/POS/src/components/sale/CouponDialog.vue index 65ab34de4..5f82b3592 100644 --- a/POS/src/components/sale/CouponDialog.vue +++ b/POS/src/components/sale/CouponDialog.vue @@ -231,6 +231,7 @@ diff --git a/POS/src/components/common/SelectInput.vue b/POS/src/components/common/SelectInput.vue index 39f52e6e0..a4f170f4c 100644 --- a/POS/src/components/common/SelectInput.vue +++ b/POS/src/components/common/SelectInput.vue @@ -84,19 +84,45 @@ :ref="(el) => (optionRefs[index] = el)" tabindex="0" role="option" - :aria-selected="option.value === modelValue" + :aria-selected="isSelected(option.value)" class="px-2 py-1.5 text-base cursor-pointer text-start transition-colors focus:outline-none" :class=" - option.value === modelValue + isSelected(option.value) ? 'bg-blue-50 text-blue-700' : 'text-gray-800 hover:bg-gray-100 focus:bg-gray-100' " > -
- {{ option.label }} - {{ option.subtitle }} +
+ + + + + +
+ {{ option.label }} + {{ option.subtitle }} +
+ {{ option.label }}
- {{ option.label }}
@@ -115,7 +141,7 @@ defineOptions({ const props = defineProps({ modelValue: { - type: [String, Number], + type: [String, Number, Array], default: "", }, options: { @@ -150,6 +176,10 @@ const props = defineProps({ type: Number, default: 50, // Limit displayed options for performance }, + multiple: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(["update:modelValue", "change"]); @@ -163,11 +193,31 @@ const optionRefs = ref([]); const dropdownPosition = ref({ top: 0, left: 0, width: 0 }); const searchQuery = ref(""); +const selectedValues = computed(() => { + if (props.multiple) { + return Array.isArray(props.modelValue) ? props.modelValue : []; + } + return props.modelValue === 0 || props.modelValue ? [props.modelValue] : []; +}); + const selectedLabel = computed(() => { + if (props.multiple) { + const count = selectedValues.value.length; + if (!count) return ""; + if (count === 1) { + const selected = props.options.find((opt) => opt.value === selectedValues.value[0]); + return selected?.label || String(selectedValues.value[0]); + } + return `${count} selected`; + } const selected = props.options.find((opt) => opt.value === props.modelValue); return selected?.label || ""; }); +function isSelected(value) { + return selectedValues.value.includes(value); +} + const filteredOptions = computed(() => { let result = props.options; @@ -237,6 +287,18 @@ function openAndFocusFirst() { } function selectOption(option) { + if (props.multiple) { + const current = [...selectedValues.value]; + const idx = current.indexOf(option.value); + if (idx >= 0) { + current.splice(idx, 1); + } else { + current.push(option.value); + } + emit("update:modelValue", current); + emit("change", current); + return; + } emit("update:modelValue", option.value); emit("change", option.value); close(); diff --git a/POS/src/components/sale/CreateCustomerDialog.vue b/POS/src/components/sale/CreateCustomerDialog.vue index cd929f1d3..ee3831765 100644 --- a/POS/src/components/sale/CreateCustomerDialog.vue +++ b/POS/src/components/sale/CreateCustomerDialog.vue @@ -9,7 +9,7 @@