@feedmepos/mf-order-setting 0.0.84 → 0.0.85
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/DisplaySystemView-hmlT9gsb.js +6485 -0
- package/dist/{KioskDevicesView-DdwFuenU.js → KioskDevicesView-fW6M9Hao.js} +1 -1
- package/dist/{KioskDevicesView.vue_vue_type_script_setup_true_lang-CxtLm_oh.js → KioskDevicesView.vue_vue_type_script_setup_true_lang-GrsnYo80.js} +3 -3
- package/dist/KioskSettingView-BdEPftHH.js +839 -0
- package/dist/{KioskView-fwBwUGCQ.js → KioskView-Bwk9S0tc.js} +82 -70
- package/dist/{OrderSettingsView-RhlyZmzs.js → OrderSettingsView-DVXfVdXj.js} +30858 -30699
- package/dist/{app-ClOkRxOZ.js → app-BnrruYco.js} +74 -12
- package/dist/app.js +1 -1
- package/dist/display-preview/assets/BandBoard-DIcfXsNV.js +1 -0
- package/dist/display-preview/assets/CarouselBlock-XAhTyEWB.js +1 -0
- package/dist/display-preview/assets/{CategoryBoard-DClMUGfS.js → CategoryBoard-DBKe18hv.js} +1 -1
- package/dist/display-preview/assets/ImageBlock-BCbnYSHq.js +1 -0
- package/dist/display-preview/assets/PickupMode-Df_TPCh8.js +1 -0
- package/dist/display-preview/assets/{StatusBoard-BVESD28J.js → StatusBoard-28z3bPOf.js} +1 -1
- package/dist/display-preview/assets/TextBlock-CvJKITtx.js +1 -0
- package/dist/display-preview/assets/VideoBlock-Dop43wDv.js +1 -0
- package/dist/display-preview/assets/index-C6DET5zJ.css +1 -0
- package/dist/display-preview/assets/index-cqMoOpSN.js +2 -0
- package/dist/display-preview/assets/pickupView-DRMVXqrc.js +1 -0
- package/dist/display-preview/index.html +2 -2
- package/dist/frontend/mf-order/src/app.d.ts +62 -0
- package/dist/frontend/mf-order/src/main.d.ts +62 -0
- package/dist/frontend/mf-order/src/modules/display-system/interface.d.ts +21 -0
- package/dist/frontend/mf-order/src/modules/order-setting/kiosk/coverCarousel.d.ts +69 -0
- package/dist/frontend/mf-order/src/modules/order-setting/kiosk/interface.d.ts +5 -1
- package/dist/frontend/mf-order/src/stores/order-setting/index.d.ts +90 -3
- package/dist/frontend/mf-order/src/views/display-system/DisplayFieldControl.vue.d.ts +3 -0
- package/dist/frontend/mf-order/src/views/display-system/DisplayInspector.vue.d.ts +3 -13
- package/dist/frontend/mf-order/src/views/order-settings/auto-send-kitchen.d.ts +58 -0
- package/dist/frontend/mf-order/src/views/order-settings/delivery/inhouse/InHouseDelivery.vue.d.ts +16 -1
- package/dist/frontend/mf-order/src/views/order-settings/pickup/PickUpSettingDialogContent.vue.d.ts +16 -1
- package/dist/frontend/mf-order/src/views/order-settings/useAutoSendGuard.d.ts +48 -0
- package/dist/frontend/mf-order/tsconfig.app.tsbuildinfo +1 -1
- package/dist/{index-CKEbr9Es.js → index-D5wABuUx.js} +1 -1
- package/dist/{index-5k7kGCpJ.js → index-D7IWjZdy.js} +2 -2
- package/dist/{index-I2YGevce.js → index-LyvBqKnv.js} +1 -1
- package/dist/{order.dto-Dbn-hRrm.js → order.dto-D7HGiueX.js} +3579 -3428
- package/dist/package/entity/alert/investigation.dto.d.ts +25 -6
- package/dist/package/entity/alert/lark-callback.dto.d.ts +31 -12
- package/dist/package/entity/alert/suggestion.dto.d.ts +4 -4
- package/dist/package/entity/display/blocks.d.ts +42 -76
- package/dist/package/entity/incoming-order/incoming-order-to-bill.dto.d.ts +47 -36
- package/dist/package/entity/incoming-order/incoming-order.dto.d.ts +1 -0
- package/dist/package/entity/index.d.ts +2 -0
- package/dist/package/entity/index.frontend.d.ts +2 -0
- package/dist/package/entity/order/effects/effect.dto.d.ts +266 -0
- package/dist/package/entity/order/order-item/order-item.dto.d.ts +36 -36
- package/dist/package/entity/order/order.do.d.ts +7 -64
- package/dist/package/entity/order/order.dto.d.ts +13006 -795
- package/dist/package/entity/order-platform/grabfood/grabfood-settings.do.d.ts +13 -0
- package/dist/package/entity/order-platform/grabfood/grabfood.dto.d.ts +3 -0
- package/dist/package/entity/order-platform/menu.dto.d.ts +10 -10
- package/dist/package/entity/order-platform/shopeefood/shopeefood-order.dto.d.ts +11 -8
- package/dist/package/entity/order-setting/auto-accept.dto.d.ts +94 -0
- package/dist/package/entity/order-setting/auto-send.resolve.d.ts +110 -0
- package/dist/package/entity/order-setting/delivery/inhouse-delivery.do.d.ts +3 -0
- package/dist/package/entity/order-setting/delivery/inhouse-delivery.dto.d.ts +3 -0
- package/dist/package/entity/order-setting/dine-in/dine-in.do.d.ts +3 -0
- package/dist/package/entity/order-setting/dine-in/dine-in.dto.d.ts +3 -0
- package/dist/package/entity/order-setting/kiosk/kiosk.do.d.ts +26 -0
- package/dist/package/entity/order-setting/kiosk/kiosk.dto.d.ts +39 -0
- package/dist/package/entity/order-setting/order-setting.do.d.ts +57 -4
- package/dist/package/entity/order-setting/order-setting.dto.d.ts +259 -9
- package/dist/package/entity/order-setting/pickup/pickup.do.d.ts +3 -0
- package/dist/package/entity/order-setting/pickup/pickup.dto.d.ts +3 -0
- package/dist/package/entity/order-setting/queue/queue.do.d.ts +2 -2
- package/dist/package/entity/order-setting/queue/queue.dto.d.ts +2 -2
- package/dist/package/entity/queue/queue.dto.d.ts +48 -48
- package/dist/package/entity/restaurant/restaurant.dto.d.ts +46 -8
- package/dist/package/entity/websocket/websocket.dto.d.ts +88 -0
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/locales/en-US.json +22 -2
- package/src/locales/ja-JP.json +12 -1
- package/src/locales/th-TH.json +12 -1
- package/src/locales/zh-CN.json +22 -2
- package/src/modules/display-system/interface.spec.ts +32 -18
- package/src/modules/display-system/interface.ts +32 -0
- package/src/modules/order-setting/kiosk/coverCarousel.spec.ts +172 -0
- package/src/modules/order-setting/kiosk/coverCarousel.ts +186 -0
- package/src/modules/order-setting/kiosk/interface.ts +138 -127
- package/src/stores/order-setting/mapper.spec.ts +26 -0
- package/src/stores/order-setting/mapper.ts +305 -293
- package/src/views/display-system/DisplayFieldControl.vue +46 -0
- package/src/views/display-system/DisplayInspector.vue +8 -3
- package/src/views/display-system/DisplaySystemView.vue +7 -3
- package/src/views/display-system/DisplayTemplateEditor.vue +28 -2
- package/src/views/kiosk/settings/KioskSettingView.vue +176 -60
- package/src/views/order-settings/auto-send-kitchen.spec.ts +137 -0
- package/src/views/order-settings/auto-send-kitchen.ts +92 -0
- package/src/views/order-settings/auto-send-resolve.spec.ts +123 -0
- package/src/views/order-settings/delivery/DeliverySetting.vue +10 -0
- package/src/views/order-settings/delivery/inhouse/InHouseDelivery.vue +40 -5
- package/src/views/order-settings/dine-in/DineInSetting.vue +38 -5
- package/src/views/order-settings/pickup/PickUpSetting.vue +1 -0
- package/src/views/order-settings/pickup/PickUpSettingDialogContent.vue +38 -5
- package/src/views/order-settings/queue/QueueSetting.vue +6 -9
- package/src/views/order-settings/useAutoSendGuard.ts +142 -0
- package/tsconfig.app.json +10 -1
- package/dist/DisplaySystemView-4Bz8p26A.js +0 -5892
- package/dist/KioskSettingView-D57KtQ-t.js +0 -711
- package/dist/display-preview/assets/BandBoard-BrDTYDc9.js +0 -1
- package/dist/display-preview/assets/CarouselBlock-CrkGxoGk.js +0 -1
- package/dist/display-preview/assets/ImageBlock-DtgfHd5G.js +0 -1
- package/dist/display-preview/assets/PickupMode-DKmqGlG8.js +0 -1
- package/dist/display-preview/assets/TextBlock-gAfeCCmE.js +0 -1
- package/dist/display-preview/assets/index-BB0ReVXy.js +0 -2
- package/dist/display-preview/assets/index-BWVpFSIw.css +0 -1
- package/dist/display-preview/assets/pickupView-C-BGcOu8.js +0 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { OrderSettingChannelAutoAccept } from '@entity'
|
|
2
|
+
import { resolveAutoAccept } from '@entity'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Portal-side helpers for the per-channel "Auto Send Kitchen" control.
|
|
6
|
+
*
|
|
7
|
+
* The RESOLUTION itself is not here — it lives in
|
|
8
|
+
* `@entity/order-setting/auto-send.resolve`, shared with the backend stamper so
|
|
9
|
+
* a settings page and the order it produces can never disagree. What is left
|
|
10
|
+
* here is presentation: select mapping, label keys, and when to raise the
|
|
11
|
+
* confirmation dialog.
|
|
12
|
+
*
|
|
13
|
+
* The dialog is now a teaching moment, not a safety mechanism. The backend
|
|
14
|
+
* enforces "a bill is only closed automatically if the kitchen was notified" at
|
|
15
|
+
* the point of resolution, which covers the paths no page can reach — old
|
|
16
|
+
* portal bundles, direct API writes, and pairs assembled ACROSS pages (the
|
|
17
|
+
* Queue page owns queue's auto-send while Dine In owns its auto-close). The
|
|
18
|
+
* dialog survives because offering "turn on both" is a better answer than
|
|
19
|
+
* letting a merchant save something inert.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** Tri-state select value. 'null' = unset, i.e. follow the channel's auto-accept. */
|
|
23
|
+
export type AutoSendSelectValue = 'null' | 'true' | 'false'
|
|
24
|
+
|
|
25
|
+
export function autoSendSelectValue(
|
|
26
|
+
autoSendKitchen: boolean | null | undefined
|
|
27
|
+
): AutoSendSelectValue {
|
|
28
|
+
return autoSendKitchen === null || autoSendKitchen === undefined
|
|
29
|
+
? 'null'
|
|
30
|
+
: (String(autoSendKitchen) as 'true' | 'false')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function autoSendFromSelectValue(v: string): boolean | null {
|
|
34
|
+
return v === 'null' ? null : v === 'true'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* True when turning auto-close on would have NO effect, so the merchant should
|
|
39
|
+
* be offered kitchen-send alongside it.
|
|
40
|
+
*
|
|
41
|
+
* Evaluated for a PAID order deliberately. An unpaid bill never auto-closes
|
|
42
|
+
* anyway (`closeBill` no-ops while `balance > 0`), so the only question worth
|
|
43
|
+
* asking is whether the setting does anything for the orders that CAN close.
|
|
44
|
+
* That keeps paid-only channels dialog-free — their paid orders do reach the
|
|
45
|
+
* kitchen — while still asking on a channel whose auto-accept is off entirely.
|
|
46
|
+
*
|
|
47
|
+
* An EXPLICIT choice decides on its own, before the channel is consulted. When
|
|
48
|
+
* the merchant has picked Enabled or Disabled (or dine-in has, for a channel
|
|
49
|
+
* that inherits), `resolveAutoSend` never reaches the auto-accept fallback, so
|
|
50
|
+
* the answer is fully determined and reading the channel would only be a chance
|
|
51
|
+
* to get it wrong. Checking the channel first is precisely the regression this
|
|
52
|
+
* guard shipped with: a stored `Disabled` plus an unreadable channel produced
|
|
53
|
+
* no dialog, no warning sublabel, and no reverse-clear — the merchant saved the
|
|
54
|
+
* inert pair with no feedback anywhere.
|
|
55
|
+
*
|
|
56
|
+
* Only a genuinely unresolved value falls through to the channel, and an
|
|
57
|
+
* unknown channel then fails OPEN: the backend invariant is the safety net now,
|
|
58
|
+
* so a page that cannot read the channel must not nag on a guess.
|
|
59
|
+
*
|
|
60
|
+
* `inheritFrom` carries dine-in's value for the channels that inherit it
|
|
61
|
+
* (queue, kiosk); omit it for channels that own their setting.
|
|
62
|
+
*/
|
|
63
|
+
export function requiresAutoSendConfirmation(
|
|
64
|
+
autoSendKitchen: boolean | null | undefined,
|
|
65
|
+
channel: OrderSettingChannelAutoAccept | null | undefined,
|
|
66
|
+
inheritFrom?: boolean | null
|
|
67
|
+
): boolean {
|
|
68
|
+
const explicit = autoSendKitchen ?? inheritFrom
|
|
69
|
+
if (explicit !== null && explicit !== undefined) return !explicit
|
|
70
|
+
const autoAccept = resolveAutoAccept(channel, true)
|
|
71
|
+
if (autoAccept === null) return false
|
|
72
|
+
return !autoAccept
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* i18n key for the auto-close sublabel.
|
|
77
|
+
*
|
|
78
|
+
* A merchant can arrive already in the broken state — auto-close stored on,
|
|
79
|
+
* kitchen send resolving false — without ever touching a control, so the
|
|
80
|
+
* confirmation dialog never fires for them. This row is the only thing that
|
|
81
|
+
* tells them, which is why it reads the STORED pair rather than a change.
|
|
82
|
+
*/
|
|
83
|
+
export function autoCloseSublabelKey(
|
|
84
|
+
autoSendKitchen: boolean | null | undefined,
|
|
85
|
+
channel: OrderSettingChannelAutoAccept | null | undefined,
|
|
86
|
+
autoCloseBill: boolean | null | undefined,
|
|
87
|
+
inheritFrom?: boolean | null
|
|
88
|
+
): string {
|
|
89
|
+
return autoCloseBill && requiresAutoSendConfirmation(autoSendKitchen, channel, inheritFrom)
|
|
90
|
+
? 'order.autoCloseBillRequiresAutoSend'
|
|
91
|
+
: 'order.autoCloseBillSublabel'
|
|
92
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type { OrderSettingChannelAutoAccept } from '@entity'
|
|
3
|
+
import { resolveAutoAccept, resolveAutoCloseBill, resolveAutoSend } from '@entity'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The resolver is the ONLY definition of what a channel does to an order, and
|
|
7
|
+
* every producer of an IncomingOrder config calls it, so these are the rules the
|
|
8
|
+
* stamped config is held to.
|
|
9
|
+
*
|
|
10
|
+
* Ground truth for the auto-accept column is `isAutoAcceptable`
|
|
11
|
+
* (backend/libs/incoming-order/incoming-order.service.ts): paid-only returns
|
|
12
|
+
* `hasSucceedPayment(order)`, so an UNPAID order on a paid-only channel is
|
|
13
|
+
* accepted by hand, not automatically.
|
|
14
|
+
*/
|
|
15
|
+
const OFF: OrderSettingChannelAutoAccept = { autoAccept: false, autoAcceptPaidOnly: false }
|
|
16
|
+
const ON: OrderSettingChannelAutoAccept = { autoAccept: true, autoAcceptPaidOnly: false }
|
|
17
|
+
const PAID_ONLY: OrderSettingChannelAutoAccept = { autoAccept: true, autoAcceptPaidOnly: true }
|
|
18
|
+
|
|
19
|
+
describe('resolveAutoAccept', () => {
|
|
20
|
+
it('is unknown, never false, when the channel could not be read', () => {
|
|
21
|
+
// Failing OPEN matters: a restaurant whose CouchDB documents do not exist
|
|
22
|
+
// yet must not have its settings page claim auto-accept is off.
|
|
23
|
+
expect(resolveAutoAccept(null, true)).toBeNull()
|
|
24
|
+
expect(resolveAutoAccept(undefined, false)).toBeNull()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('ignores payment unless the channel is paid-only', () => {
|
|
28
|
+
expect(resolveAutoAccept(OFF, true)).toBe(false)
|
|
29
|
+
expect(resolveAutoAccept(OFF, false)).toBe(false)
|
|
30
|
+
expect(resolveAutoAccept(ON, true)).toBe(true)
|
|
31
|
+
expect(resolveAutoAccept(ON, false)).toBe(true)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('makes paid-only a per-ORDER verdict', () => {
|
|
35
|
+
expect(resolveAutoAccept(PAID_ONLY, true)).toBe(true)
|
|
36
|
+
expect(resolveAutoAccept(PAID_ONLY, false)).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('ignores a stranded paidOnly when auto-accept is off', () => {
|
|
40
|
+
// `{autoAccept: false, autoAcceptPaidOnly: true}` is a real stored shape —
|
|
41
|
+
// the two POS checkboxes project one pair, so turning paid-only on and then
|
|
42
|
+
// auto-accept off leaves paidOnly behind. `isAutoAcceptable` gates on
|
|
43
|
+
// `?.autoAccept` before it ever reads paidOnly and falls through to
|
|
44
|
+
// `return false`, so the stranded flag is dead there; this must agree, or
|
|
45
|
+
// the preview would promise auto-accept for prepaid orders that get none.
|
|
46
|
+
const STRANDED = { autoAccept: false, autoAcceptPaidOnly: true }
|
|
47
|
+
expect(resolveAutoAccept(STRANDED, true)).toBe(false)
|
|
48
|
+
expect(resolveAutoAccept(STRANDED, false)).toBe(false)
|
|
49
|
+
// i.e. indistinguishable from plain "off" for every order.
|
|
50
|
+
expect(resolveAutoAccept(STRANDED, true)).toBe(resolveAutoAccept(OFF, true))
|
|
51
|
+
expect(resolveAutoAccept(STRANDED, false)).toBe(resolveAutoAccept(OFF, false))
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('resolveAutoSend', () => {
|
|
56
|
+
it('falls back to auto-accept when unset — the legacy binding, unchanged', () => {
|
|
57
|
+
expect(resolveAutoSend(null, true)).toBe(true)
|
|
58
|
+
expect(resolveAutoSend(undefined, false)).toBe(false)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('lets an explicit false WIN over a channel that auto-accepts', () => {
|
|
62
|
+
// `||` here would silently discard the merchant's choice; `??` is load-bearing.
|
|
63
|
+
expect(resolveAutoSend(false, true)).toBe(false)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('lets an explicit true decouple the kitchen from manual accept', () => {
|
|
67
|
+
// The workflow this branch exists to enable: a human accepts each order,
|
|
68
|
+
// the kitchen still fires automatically.
|
|
69
|
+
expect(resolveAutoSend(true, false)).toBe(true)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('consults inheritFrom before auto-accept, and only when unset', () => {
|
|
73
|
+
expect(resolveAutoSend(null, true, false)).toBe(false)
|
|
74
|
+
expect(resolveAutoSend(null, false, true)).toBe(true)
|
|
75
|
+
expect(resolveAutoSend(false, true, true)).toBe(false)
|
|
76
|
+
expect(resolveAutoSend(true, false, false)).toBe(true)
|
|
77
|
+
// A null inheritFrom must fall through rather than pin anything.
|
|
78
|
+
expect(resolveAutoSend(null, true, null)).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('resolveAutoCloseBill — the invariant', () => {
|
|
83
|
+
it('never closes a bill the kitchen was not told about', () => {
|
|
84
|
+
expect(resolveAutoCloseBill(true, false)).toBe(false)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('leaves every coherent combination alone', () => {
|
|
88
|
+
expect(resolveAutoCloseBill(true, true)).toBe(true)
|
|
89
|
+
expect(resolveAutoCloseBill(false, true)).toBe(false)
|
|
90
|
+
expect(resolveAutoCloseBill(null, true)).toBe(false)
|
|
91
|
+
expect(resolveAutoCloseBill(undefined, false)).toBe(false)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
describe('the reported bug and its fix, end to end through the chain', () => {
|
|
97
|
+
it('suppresses the close that used to happen with the kitchen never notified', () => {
|
|
98
|
+
// Auto-accept off, merchant turned auto-close on: the exact merchant report.
|
|
99
|
+
// Before the invariant this stamped `autoCloseBill: true` and POS closed the
|
|
100
|
+
// paid bill without ever sending it.
|
|
101
|
+
const autoAccept = resolveAutoAccept(OFF, true) as boolean
|
|
102
|
+
const autoSend = resolveAutoSend(null, autoAccept)
|
|
103
|
+
expect(autoSend).toBe(false)
|
|
104
|
+
expect(resolveAutoCloseBill(true, autoSend)).toBe(false)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('gives the merchant the workflow they asked for once kitchen send is on', () => {
|
|
108
|
+
// Manual accept, auto send, auto complete.
|
|
109
|
+
const autoAccept = resolveAutoAccept(OFF, true) as boolean
|
|
110
|
+
const autoSend = resolveAutoSend(true, autoAccept)
|
|
111
|
+
expect(autoAccept).toBe(false)
|
|
112
|
+
expect(autoSend).toBe(true)
|
|
113
|
+
expect(resolveAutoCloseBill(true, autoSend)).toBe(true)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('closes the queue hole: an explicit queue false cannot outlive the invariant', () => {
|
|
117
|
+
// queueSettings.autoSendKitchen = false beats the pinned dine-in true, which
|
|
118
|
+
// is how the Queue page reopened the bug — but the close is suppressed now.
|
|
119
|
+
const autoSend = resolveAutoSend(false, true, true)
|
|
120
|
+
expect(autoSend).toBe(false)
|
|
121
|
+
expect(resolveAutoCloseBill(true, autoSend)).toBe(false)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
:initial-value="inHouseComponentProps.initialValue"
|
|
106
106
|
:address="formatAddress((currentRestaurant as any).profile.address)"
|
|
107
107
|
:order-inhouse-delivery-settings="orderInhouseDeliverySettings"
|
|
108
|
+
:channel-auto-accept="orderSetting?.channelAutoAccept?.inhouseDelivery ?? null"
|
|
108
109
|
@update:model-value="handleModelValueUpdateInHouse"
|
|
109
110
|
@update:order-settings="handleOrderInhouseDeliverySettingsUpdate"
|
|
110
111
|
/>
|
|
@@ -487,6 +488,15 @@ async function updateFeedmeExpress({ key, setting }: IntegratedDeliveryRow) {
|
|
|
487
488
|
async function updateInHouse({ key, setting }: IntegratedDeliveryRow) {
|
|
488
489
|
clearComponentProps()
|
|
489
490
|
|
|
491
|
+
// Drop the previous restaurant's settings BEFORE mounting the side sheet.
|
|
492
|
+
// The mount below is synchronous while the GET at the end of this function is
|
|
493
|
+
// not, so a stale value here would be read by `InHouseDelivery`'s auto-send
|
|
494
|
+
// guard — which reads `channelAutoAccept` — and it would decide whether to
|
|
495
|
+
// raise the auto-close dialog against the restaurant the merchant just
|
|
496
|
+
// navigated away from. Undefined means "unknown", which fails open and claims
|
|
497
|
+
// nothing until the real data lands.
|
|
498
|
+
orderSetting.value = undefined
|
|
499
|
+
|
|
490
500
|
const inhouseSetting = setting as FdoRestaurantInHouseDelivery
|
|
491
501
|
|
|
492
502
|
inHouseComponentProps.value = {
|
|
@@ -9,7 +9,8 @@ import type {
|
|
|
9
9
|
FdoOfflinePaymentMethod,
|
|
10
10
|
FdoRestaurantInHouseDelivery
|
|
11
11
|
} from '@feedmepos/core/entity'
|
|
12
|
-
import type { OrderInhouseDeliverySettingsDto } from '@entity'
|
|
12
|
+
import type { OrderInhouseDeliverySettingsDto, OrderSettingChannelAutoAccept } from '@entity'
|
|
13
|
+
import { useAutoSendGuard } from '../../useAutoSendGuard'
|
|
13
14
|
import { F_FEATURE } from '@feedmepos/core/entity'
|
|
14
15
|
import { FdoProductTaxSetting } from '@feedmepos/entity'
|
|
15
16
|
import { computed, reactive, watch, type PropType } from 'vue'
|
|
@@ -81,12 +82,33 @@ const props = defineProps({
|
|
|
81
82
|
orderInhouseDeliverySettings: {
|
|
82
83
|
type: Object as PropType<OrderInhouseDeliverySettingsDto>,
|
|
83
84
|
default: () => ({})
|
|
85
|
+
},
|
|
86
|
+
// Read-only projection of `setting.inHouseDelivery`'s auto-accept pair,
|
|
87
|
+
// injected on the order-settings read. null = the backend could not resolve
|
|
88
|
+
// it; the auto-close guard then fails OPEN rather than disabling on a guess.
|
|
89
|
+
channelAutoAccept: {
|
|
90
|
+
type: Object as PropType<OrderSettingChannelAutoAccept | null>,
|
|
91
|
+
default: null
|
|
84
92
|
}
|
|
85
93
|
})
|
|
86
94
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
// Select + auto-close coupling live in one shared implementation; this view only
|
|
96
|
+
// says how to PERSIST a change. `null` stays unset so the backend keeps
|
|
97
|
+
// inheriting `setting.inHouseDelivery`'s auto-accept — `false` would pin it.
|
|
98
|
+
const {
|
|
99
|
+
autoSendKitchenValue,
|
|
100
|
+
selectItems: autoSendSelectItems,
|
|
101
|
+
autoCloseSublabel,
|
|
102
|
+
autoCloseSwitchKey,
|
|
103
|
+
updateAutoSendKitchen,
|
|
104
|
+
updateAutoCloseBill
|
|
105
|
+
} = useAutoSendGuard({
|
|
106
|
+
channel: () => props.channelAutoAccept,
|
|
107
|
+
autoSendKitchen: () => props.orderInhouseDeliverySettings.autoSendKitchen,
|
|
108
|
+
autoCloseBill: () => props.orderInhouseDeliverySettings.autoCloseBill,
|
|
109
|
+
apply: (patch) =>
|
|
110
|
+
emits('update:order-settings', { ...props.orderInhouseDeliverySettings, ...patch })
|
|
111
|
+
})
|
|
90
112
|
|
|
91
113
|
function updatePrintReceiptAfterClose(printReceiptAfterClose: boolean) {
|
|
92
114
|
emits('update:order-settings', { ...props.orderInhouseDeliverySettings, printReceiptAfterClose })
|
|
@@ -269,12 +291,25 @@ const actualDeliveryTime = computed({
|
|
|
269
291
|
/>
|
|
270
292
|
</div>
|
|
271
293
|
|
|
294
|
+
<div class="mb-5 flex flex-col gap-2">
|
|
295
|
+
<span class="fm-typo-en-body-md-600">{{ t('order.autoSendKitchen') }}</span>
|
|
296
|
+
<span class="fm-typo-en-body-sm-400 text-fm-color-typo-secondary">
|
|
297
|
+
{{ t('order.autoSendKitchenChannelSublabel') }}
|
|
298
|
+
</span>
|
|
299
|
+
<FmSelect
|
|
300
|
+
:model-value="autoSendKitchenValue"
|
|
301
|
+
:items="autoSendSelectItems"
|
|
302
|
+
@update:model-value="updateAutoSendKitchen"
|
|
303
|
+
/>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
272
306
|
<div class="mb-5">
|
|
273
307
|
<FmSwitch
|
|
308
|
+
:key="autoCloseSwitchKey"
|
|
274
309
|
:model-value="orderInhouseDeliverySettings.autoCloseBill ?? false"
|
|
275
310
|
value="autoCloseBill"
|
|
276
311
|
:label="t('order.autoCloseBill')"
|
|
277
|
-
:sublabel="
|
|
312
|
+
:sublabel="autoCloseSublabel"
|
|
278
313
|
label-placement="right"
|
|
279
314
|
@update:model-value="updateAutoCloseBill"
|
|
280
315
|
/>
|
|
@@ -115,10 +115,23 @@
|
|
|
115
115
|
@update:model-value="updateOverridePax"
|
|
116
116
|
></FmSwitch>
|
|
117
117
|
|
|
118
|
+
<div class="flex flex-col gap-2">
|
|
119
|
+
<span class="fm-typo-en-body-md-600">{{ t('order.autoSendKitchen') }}</span>
|
|
120
|
+
<span class="fm-typo-en-body-sm-400 text-fm-color-typo-secondary">
|
|
121
|
+
{{ t('order.autoSendKitchenChannelSublabel') }}
|
|
122
|
+
</span>
|
|
123
|
+
<FmSelect
|
|
124
|
+
:model-value="autoSendKitchenValue"
|
|
125
|
+
:items="autoSendSelectItems"
|
|
126
|
+
@update:model-value="updateAutoSendKitchen"
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
118
130
|
<FmSwitch
|
|
131
|
+
:key="autoCloseSwitchKey"
|
|
119
132
|
:model-value="orderSetting?.dineInSettings?.autoCloseBill ?? false"
|
|
120
133
|
:label="t('order.autoCloseBill')"
|
|
121
|
-
:sublabel="
|
|
134
|
+
:sublabel="autoCloseSublabel"
|
|
122
135
|
label-placement="right"
|
|
123
136
|
@update:model-value="updateAutoCloseBill"
|
|
124
137
|
></FmSwitch>
|
|
@@ -173,6 +186,7 @@ import semver from 'semver'
|
|
|
173
186
|
import { useI18n } from '@feedmepos/mf-common'
|
|
174
187
|
import { OrderSettingApi } from '@/api/order-setting'
|
|
175
188
|
import { type OrderSettingsDto } from '@entity'
|
|
189
|
+
import { useAutoSendGuard } from '../useAutoSendGuard'
|
|
176
190
|
import { useSnackbarFunctions } from '@/components/snackbar'
|
|
177
191
|
|
|
178
192
|
const { t } = useI18n()
|
|
@@ -270,10 +284,29 @@ function updateOverridePax(v: boolean) {
|
|
|
270
284
|
if (dineInSettings) dineInSettings.overridePax = v
|
|
271
285
|
}
|
|
272
286
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
287
|
+
const dineInAutoAccept = computed(() => orderSetting.value?.channelAutoAccept?.dineIn ?? null)
|
|
288
|
+
|
|
289
|
+
// Select + auto-close coupling live in one shared implementation; this view only
|
|
290
|
+
// says how to PERSIST a change. `null` stays unset so the backend keeps
|
|
291
|
+
// inheriting the channel's auto-accept — writing `false` would pin it.
|
|
292
|
+
const {
|
|
293
|
+
autoSendKitchenValue,
|
|
294
|
+
selectItems: autoSendSelectItems,
|
|
295
|
+
autoCloseSublabel,
|
|
296
|
+
autoCloseSwitchKey,
|
|
297
|
+
updateAutoSendKitchen,
|
|
298
|
+
updateAutoCloseBill
|
|
299
|
+
} = useAutoSendGuard({
|
|
300
|
+
channel: () => dineInAutoAccept.value,
|
|
301
|
+
autoSendKitchen: () => orderSetting.value?.dineInSettings?.autoSendKitchen,
|
|
302
|
+
autoCloseBill: () => orderSetting.value?.dineInSettings?.autoCloseBill,
|
|
303
|
+
apply: (patch) => {
|
|
304
|
+
const settings = ensureOrderDineInSettings()
|
|
305
|
+
if (!settings) return
|
|
306
|
+
if ('autoSendKitchen' in patch) settings.autoSendKitchen = patch.autoSendKitchen ?? null
|
|
307
|
+
if ('autoCloseBill' in patch) settings.autoCloseBill = patch.autoCloseBill ?? false
|
|
308
|
+
}
|
|
309
|
+
})
|
|
277
310
|
|
|
278
311
|
function updatePrintReceiptAfterClose(v: boolean) {
|
|
279
312
|
const dineInSettings = ensureOrderDineInSettings()
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<PickUpSettingDialogContent
|
|
8
8
|
:pickup="pickupSetting"
|
|
9
9
|
:order-pickup-settings="orderPickupSettings"
|
|
10
|
+
:channel-auto-accept="orderSetting?.channelAutoAccept?.pickup ?? null"
|
|
10
11
|
:accept-pickup="acceptPickup"
|
|
11
12
|
@update-pickup="updatePickupSetting"
|
|
12
13
|
@update-order-settings="updateOrderPickupSettings"
|
|
@@ -19,12 +19,24 @@
|
|
|
19
19
|
label-placement="right"
|
|
20
20
|
@update:model-value="updateIgnoreStock"
|
|
21
21
|
/>
|
|
22
|
+
<div class="flex flex-col gap-2 mt-5">
|
|
23
|
+
<span class="fm-typo-en-body-md-600">{{ t('order.autoSendKitchen') }}</span>
|
|
24
|
+
<span class="fm-typo-en-body-sm-400 text-fm-color-typo-secondary">
|
|
25
|
+
{{ t('order.autoSendKitchenChannelSublabel') }}
|
|
26
|
+
</span>
|
|
27
|
+
<FmSelect
|
|
28
|
+
:model-value="autoSendKitchenValue"
|
|
29
|
+
:items="autoSendSelectItems"
|
|
30
|
+
@update:model-value="updateAutoSendKitchen"
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
22
33
|
<FmSwitch
|
|
23
34
|
class="mt-5"
|
|
35
|
+
:key="autoCloseSwitchKey"
|
|
24
36
|
:model-value="orderPickupSettings.autoCloseBill ?? false"
|
|
25
37
|
value="autoCloseBill"
|
|
26
38
|
:label="t('order.autoCloseBill')"
|
|
27
|
-
:sublabel="
|
|
39
|
+
:sublabel="autoCloseSublabel"
|
|
28
40
|
label-placement="right"
|
|
29
41
|
@update:model-value="updateAutoCloseBill"
|
|
30
42
|
/>
|
|
@@ -89,7 +101,8 @@ import {
|
|
|
89
101
|
FdoPickupPoint,
|
|
90
102
|
FdoRestaurantPickup
|
|
91
103
|
} from '@feedmepos/core/entity'
|
|
92
|
-
import type { OrderPickupSettingsDto } from '@entity'
|
|
104
|
+
import type { OrderPickupSettingsDto, OrderSettingChannelAutoAccept } from '@entity'
|
|
105
|
+
import { useAutoSendGuard } from '../useAutoSendGuard'
|
|
93
106
|
|
|
94
107
|
import { computed, ref, watch, type PropType } from 'vue'
|
|
95
108
|
import PickUpPointDialog from './PickUpPointDialog.vue'
|
|
@@ -119,6 +132,13 @@ const props = defineProps({
|
|
|
119
132
|
type: Object as PropType<OrderPickupSettingsDto>,
|
|
120
133
|
default: () => ({})
|
|
121
134
|
},
|
|
135
|
+
// Read-only projection of `setting.pickup`'s auto-accept pair, injected on the
|
|
136
|
+
// order-settings read. null = the backend could not resolve it; the auto-close
|
|
137
|
+
// guard then fails OPEN rather than disabling the toggle on a guess.
|
|
138
|
+
channelAutoAccept: {
|
|
139
|
+
type: Object as PropType<OrderSettingChannelAutoAccept | null>,
|
|
140
|
+
default: null
|
|
141
|
+
},
|
|
122
142
|
// Read-only pickup online status (restaurant-level `acceptPickup`); shown as a
|
|
123
143
|
// labeled row at the top of this section, not editable here.
|
|
124
144
|
acceptPickup: {
|
|
@@ -133,9 +153,22 @@ const getClassForOnline = (enabled: boolean) => {
|
|
|
133
153
|
: 'bg-fm-color-neutral-gray-100 text-fm-color-typo-neutral fm-typo-en-body-md-600 border fm-corner-radius-lg w-max p-4'
|
|
134
154
|
}
|
|
135
155
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
156
|
+
// Select + auto-close coupling live in one shared implementation; this view only
|
|
157
|
+
// says how to PERSIST a change. `null` stays unset so the backend keeps
|
|
158
|
+
// inheriting `setting.pickup`'s auto-accept — writing `false` would pin it.
|
|
159
|
+
const {
|
|
160
|
+
autoSendKitchenValue,
|
|
161
|
+
selectItems: autoSendSelectItems,
|
|
162
|
+
autoCloseSublabel,
|
|
163
|
+
autoCloseSwitchKey,
|
|
164
|
+
updateAutoSendKitchen,
|
|
165
|
+
updateAutoCloseBill
|
|
166
|
+
} = useAutoSendGuard({
|
|
167
|
+
channel: () => props.channelAutoAccept,
|
|
168
|
+
autoSendKitchen: () => props.orderPickupSettings.autoSendKitchen,
|
|
169
|
+
autoCloseBill: () => props.orderPickupSettings.autoCloseBill,
|
|
170
|
+
apply: (patch) => emits('update-order-settings', { ...props.orderPickupSettings, ...patch })
|
|
171
|
+
})
|
|
139
172
|
|
|
140
173
|
function updatePrintReceiptAfterClose(printReceiptAfterClose: boolean) {
|
|
141
174
|
emits('update-order-settings', { ...props.orderPickupSettings, printReceiptAfterClose })
|
|
@@ -4,7 +4,6 @@ import { OrderSettingApi } from '@/api/order-setting'
|
|
|
4
4
|
import { restaurantApi, type RestaurantSettingDoc } from '@/api/restaurant-setting'
|
|
5
5
|
import { useLoading } from '@/composables/loading'
|
|
6
6
|
import {
|
|
7
|
-
type FdoOrderSettings,
|
|
8
7
|
type FdoOrderQueueSettings,
|
|
9
8
|
defaultQueueSetting,
|
|
10
9
|
OrderSettingsDto,
|
|
@@ -16,13 +15,16 @@ import { useSnackbarFunctions } from '@/components/snackbar'
|
|
|
16
15
|
import { useDialog } from '@feedmepos/ui-library'
|
|
17
16
|
import PresetRemarksSheet from './PresetRemarksSheet.vue'
|
|
18
17
|
import { nextCodeSettingBand } from './code-setting-band'
|
|
18
|
+
import { autoSendFromSelectValue, autoSendSelectValue } from '../auto-send-kitchen'
|
|
19
19
|
|
|
20
20
|
const { t } = useI18n()
|
|
21
21
|
const { showSuccess, showError } = useSnackbarFunctions()
|
|
22
22
|
const { currentRestaurant } = useCoreStore()
|
|
23
23
|
const { isLoading, startAsyncCallWithErr, startLoading } = useLoading()
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// The DTO, not the stored document. `OrderSettingUpdateDto` omits the injected
|
|
26
|
+
// read-only `channelAutoAccept`, so it can never be echoed back into Mongo.
|
|
27
|
+
const orderSettingDefault = ref<OrderSettingsDto | undefined>(undefined)
|
|
26
28
|
const queueSettings = ref<FdoOrderQueueSettings>({ ...defaultQueueSetting })
|
|
27
29
|
const restaurantSetting = ref<RestaurantSettingDoc | undefined>(undefined)
|
|
28
30
|
const presetRemarks = ref<string[]>([])
|
|
@@ -444,14 +446,9 @@ async function saveAllowOverrideQueueCode(value: boolean) {
|
|
|
444
446
|
<div class="flex flex-col gap-2">
|
|
445
447
|
<span class="fm-typo-en-body-md-600">{{ t('order.autoSendKitchen') }}</span>
|
|
446
448
|
<FmSelect
|
|
447
|
-
:model-value="
|
|
448
|
-
queueSettings.autoSendKitchen === null
|
|
449
|
-
? 'null'
|
|
450
|
-
: String(queueSettings.autoSendKitchen)
|
|
451
|
-
"
|
|
449
|
+
:model-value="autoSendSelectValue(queueSettings.autoSendKitchen)"
|
|
452
450
|
@update:model-value="
|
|
453
|
-
(v: string) =>
|
|
454
|
-
updateQueueSetting('autoSendKitchen', v === 'null' ? null : v === 'true')
|
|
451
|
+
(v: string) => updateQueueSetting('autoSendKitchen', autoSendFromSelectValue(v))
|
|
455
452
|
"
|
|
456
453
|
:items="[
|
|
457
454
|
{ label: t('order.followDineIn'), value: 'null' },
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
|
+
import type { OrderSettingChannelAutoAccept } from '@entity'
|
|
3
|
+
import { useDialog } from '@feedmepos/ui-library'
|
|
4
|
+
import { useI18n } from '@feedmepos/mf-common'
|
|
5
|
+
import {
|
|
6
|
+
autoCloseSublabelKey,
|
|
7
|
+
autoSendFromSelectValue,
|
|
8
|
+
autoSendSelectValue,
|
|
9
|
+
requiresAutoSendConfirmation
|
|
10
|
+
} from './auto-send-kitchen'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The Auto Send Kitchen select and its coupling to Auto close bill, as ONE
|
|
14
|
+
* implementation.
|
|
15
|
+
*
|
|
16
|
+
* This used to be copy-pasted into every channel view, and the copies drifted:
|
|
17
|
+
* review caught one path bumping the switch-remount key and another not, which
|
|
18
|
+
* is precisely the class of bug the remount exists to prevent.
|
|
19
|
+
*
|
|
20
|
+
* Used by the three views that own BOTH controls: dine-in, pickup and in-house
|
|
21
|
+
* delivery. Queue deliberately does not — it has no auto-close toggle of its own
|
|
22
|
+
* (queue orders take `autoCloseBill` from `dineInSettings`), so there is no
|
|
23
|
+
* event to hang the dialog on, and wiring it would mean this page writing to
|
|
24
|
+
* another page's settings. Kiosk owns both flags and SHOULD use this, but its
|
|
25
|
+
* settings block is still behind `v-if="false"` — wire it here when that block
|
|
26
|
+
* is unhidden rather than growing a fourth copy.
|
|
27
|
+
*
|
|
28
|
+
* This is guidance only. The backend enforces "a bill is only closed
|
|
29
|
+
* automatically if the kitchen was notified" at the point of resolution, so
|
|
30
|
+
* nothing here is load-bearing: a merchant who never opens this page, an old
|
|
31
|
+
* portal bundle, or a direct API write is covered regardless.
|
|
32
|
+
*
|
|
33
|
+
* Views differ only in HOW they persist, so that is the one thing they pass in:
|
|
34
|
+
* `apply` receives a partial patch and the view maps it onto its own state
|
|
35
|
+
* (a reactive settings object, or an emit).
|
|
36
|
+
*/
|
|
37
|
+
export function useAutoSendGuard(opts: {
|
|
38
|
+
/** The channel's auto-accept pair, or null when it could not be read. */
|
|
39
|
+
channel: () => OrderSettingChannelAutoAccept | null | undefined
|
|
40
|
+
autoSendKitchen: () => boolean | null | undefined
|
|
41
|
+
autoCloseBill: () => boolean | null | undefined
|
|
42
|
+
/** Dine-in's value, for the channels that inherit it (queue, kiosk). */
|
|
43
|
+
inheritFrom?: () => boolean | null | undefined
|
|
44
|
+
apply: (patch: { autoSendKitchen?: boolean | null; autoCloseBill?: boolean }) => void
|
|
45
|
+
}) {
|
|
46
|
+
const { t } = useI18n()
|
|
47
|
+
const Dialog = useDialog()
|
|
48
|
+
|
|
49
|
+
const inheritFrom = () => opts.inheritFrom?.() ?? null
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Remounts the auto-close switch so it re-reads the STORED value. FmSwitch's
|
|
53
|
+
* internal state after a click we decline to apply is not something we
|
|
54
|
+
* control, and a switch left visually on while nothing was saved is the worst
|
|
55
|
+
* outcome here.
|
|
56
|
+
*/
|
|
57
|
+
const autoCloseSwitchKey = ref(0)
|
|
58
|
+
|
|
59
|
+
const autoSendKitchenValue = computed(() => autoSendSelectValue(opts.autoSendKitchen()))
|
|
60
|
+
|
|
61
|
+
const autoCloseSublabel = computed(() =>
|
|
62
|
+
t(
|
|
63
|
+
autoCloseSublabelKey(
|
|
64
|
+
opts.autoSendKitchen(),
|
|
65
|
+
opts.channel(),
|
|
66
|
+
opts.autoCloseBill(),
|
|
67
|
+
inheritFrom()
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
// Static labels: the unset option says what it INHERITS from, not what that
|
|
73
|
+
// currently evaluates to. Resolving it here meant the label depended on the
|
|
74
|
+
// async settings GET, which is why the select needed a remount key to repaint
|
|
75
|
+
// — and the resolved suffix was the thing that made a simple control look
|
|
76
|
+
// complicated. The backend is what guarantees the outcome now.
|
|
77
|
+
const selectItems = computed(() => [
|
|
78
|
+
{ label: t('order.followAutoAccept'), value: 'null' },
|
|
79
|
+
{ label: t('order.enabled'), value: 'true' },
|
|
80
|
+
{ label: t('order.disabled'), value: 'false' }
|
|
81
|
+
])
|
|
82
|
+
|
|
83
|
+
function updateAutoSendKitchen(v: string) {
|
|
84
|
+
const autoSendKitchen = autoSendFromSelectValue(v)
|
|
85
|
+
// Reverse direction: turning kitchen send off while auto-close is ON would
|
|
86
|
+
// leave auto-close inert, so it visibly follows off. No dialog — this
|
|
87
|
+
// direction only REMOVES behaviour, it never starts printing tickets.
|
|
88
|
+
//
|
|
89
|
+
// Gated on auto-close actually being on. Without that check this wrote an
|
|
90
|
+
// explicit `autoCloseBill: false` over an UNSET value on every such change,
|
|
91
|
+
// turning "inherit" into "pinned off" — the same hazard the kiosk mapper
|
|
92
|
+
// avoids by sending `?? undefined` — and remounted the switch to show a
|
|
93
|
+
// change that had not happened.
|
|
94
|
+
const clearsAutoClose =
|
|
95
|
+
opts.autoCloseBill() === true &&
|
|
96
|
+
requiresAutoSendConfirmation(autoSendKitchen, opts.channel(), inheritFrom())
|
|
97
|
+
if (clearsAutoClose) autoCloseSwitchKey.value++
|
|
98
|
+
opts.apply(
|
|
99
|
+
clearsAutoClose ? { autoSendKitchen, autoCloseBill: false } : { autoSendKitchen }
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function updateAutoCloseBill(autoCloseBill: boolean) {
|
|
104
|
+
// Turning auto-close ON while the kitchen would not be notified is the one
|
|
105
|
+
// case worth interrupting: the setting would do nothing, and confirming is
|
|
106
|
+
// the one-click fix. The backend suppresses the close either way, so this
|
|
107
|
+
// is guidance rather than the guard it used to be.
|
|
108
|
+
if (
|
|
109
|
+
autoCloseBill &&
|
|
110
|
+
requiresAutoSendConfirmation(opts.autoSendKitchen(), opts.channel(), inheritFrom())
|
|
111
|
+
) {
|
|
112
|
+
autoCloseSwitchKey.value++
|
|
113
|
+
Dialog.open({
|
|
114
|
+
title: t('order.autoCloseBillEnableKitchenTitle'),
|
|
115
|
+
message: t('order.autoCloseBillEnableKitchenMessage'),
|
|
116
|
+
dismissAway: false,
|
|
117
|
+
primaryActions: {
|
|
118
|
+
text: t('order.autoCloseBillEnableKitchenConfirm'),
|
|
119
|
+
variant: 'primary',
|
|
120
|
+
close: true
|
|
121
|
+
},
|
|
122
|
+
secondaryActions: { text: t('order.cancel'), variant: 'tertiary', close: true }
|
|
123
|
+
}).onPrimary(() => {
|
|
124
|
+
// Pin `true` rather than leave it inherited: otherwise turning
|
|
125
|
+
// auto-accept off later would silently make auto-close inert again.
|
|
126
|
+
opts.apply({ autoSendKitchen: true, autoCloseBill: true })
|
|
127
|
+
})
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
opts.apply({ autoCloseBill })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
autoSendKitchenValue,
|
|
136
|
+
selectItems,
|
|
137
|
+
autoCloseSublabel,
|
|
138
|
+
autoCloseSwitchKey,
|
|
139
|
+
updateAutoSendKitchen,
|
|
140
|
+
updateAutoCloseBill
|
|
141
|
+
}
|
|
142
|
+
}
|
package/tsconfig.app.json
CHANGED
|
@@ -8,7 +8,16 @@
|
|
|
8
8
|
"../../common/**/*",
|
|
9
9
|
"src/**/*.json"
|
|
10
10
|
],
|
|
11
|
-
"exclude": [
|
|
11
|
+
"exclude": [
|
|
12
|
+
"src/**/__tests__/*",
|
|
13
|
+
"src/**/*.spec.ts",
|
|
14
|
+
// `include` pulls in all of `../../common` for the shared helpers, which
|
|
15
|
+
// also sweeps up the jest compatibility suites that live there. This is a
|
|
16
|
+
// Vue app: its `types` array is ["googlemaps"], so jest globals are not
|
|
17
|
+
// loaded and every `describe` / `it` / `expect` in those files is an error.
|
|
18
|
+
// They are backend tests and are type-checked by the backend gate.
|
|
19
|
+
"../../common/**/*.test.ts"
|
|
20
|
+
],
|
|
12
21
|
"compilerOptions": {
|
|
13
22
|
"rootDir": "./../..",
|
|
14
23
|
"composite": true,
|