@fayz-ai/plugin-orders 0.11.2 → 0.12.0
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/OrdersPage-IKIIQALX.js +450 -0
- package/dist/OrdersPage-IKIIQALX.js.map +1 -0
- package/dist/chunk-TYS3LCKX.js +57 -0
- package/dist/chunk-TYS3LCKX.js.map +1 -0
- package/dist/context.d.ts +9 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/data/core-supabase.d.ts +26 -0
- package/dist/data/core-supabase.d.ts.map +1 -0
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/platform.d.ts +1 -1
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +608 -38
- package/dist/index.js.map +1 -1
- package/dist/lib/floor-plan-id.d.ts +13 -0
- package/dist/lib/floor-plan-id.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/store.d.ts +5 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/types.d.ts +20 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/views/CommercialOrdersScreen.d.ts +3 -0
- package/dist/views/CommercialOrdersScreen.d.ts.map +1 -0
- package/dist/views/OrderReceivableActions.d.ts +12 -0
- package/dist/views/OrderReceivableActions.d.ts.map +1 -0
- package/dist/views/OrdersPage.d.ts +7 -1
- package/dist/views/OrdersPage.d.ts.map +1 -1
- package/dist/views/OrdersScreen.d.ts +3 -0
- package/dist/views/OrdersScreen.d.ts.map +1 -0
- package/package.json +5 -3
- package/dist/OrdersPage-CQ7O74ZW.js +0 -155
- package/dist/OrdersPage-CQ7O74ZW.js.map +0 -1
- package/dist/chunk-MTVIXIPL.js +0 -23
- package/dist/chunk-MTVIXIPL.js.map +0 -1
- package/dist/views/OrderKanbanView.d.ts +0 -3
- package/dist/views/OrderKanbanView.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { getNextStatus, STATUS_FLOW } from './chunk-
|
|
2
|
-
|
|
1
|
+
import { getNextStatus, STATUS_FLOW } from './chunk-TYS3LCKX.js';
|
|
2
|
+
export { ADVANCE_VERB, DEFAULT_STATUS_CONFIGS, KANBAN_COLUMNS, STATUS_FLOW, getNextStatus, getPreviousStatus, mapToKanbanColumn } from './chunk-TYS3LCKX.js';
|
|
3
|
+
import * as React2 from 'react';
|
|
4
|
+
import React2__default from 'react';
|
|
5
|
+
import { fayzConnectorOAuth, OrderDetailPage, orderIsEditable, PageActions, adminNavigateTo, PluginSettingsPanel, dedup, usePermissionOptional } from '@fayz-ai/admin';
|
|
6
|
+
import { Pencil } from 'lucide-react';
|
|
7
|
+
import { createConnectionStore, connectionRuns, connectionSettings, connectionStatus, settingsStatus, getSupabaseClientOptional, usePluginRuntimeOptional, emitDomainEvent, errorMessage } from '@fayz-ai/core';
|
|
3
8
|
import { createStore } from 'zustand/vanilla';
|
|
4
|
-
import { fayzConnectorOAuth, PluginSettingsPanel, dedup } from '@fayz-ai/admin';
|
|
5
9
|
import { toast } from 'sonner';
|
|
6
|
-
import {
|
|
10
|
+
import { ReceivePaymentModal } from '@fayz-ai/plugin-financial';
|
|
11
|
+
import { Button, toast as toast$1 } from '@fayz-ai/ui';
|
|
12
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
7
13
|
import { createFayzClient } from '@fayz-ai/sdk';
|
|
8
14
|
|
|
9
15
|
// src/data/mock.ts
|
|
@@ -278,7 +284,7 @@ function createMockOrdersProvider() {
|
|
|
278
284
|
const flow = STATUS_FLOW[order.kind];
|
|
279
285
|
const currentIdx = flow.indexOf(order.status);
|
|
280
286
|
const targetIdx = flow.indexOf(input.status);
|
|
281
|
-
if (targetIdx < 0 || targetIdx !== currentIdx + 1) {
|
|
287
|
+
if (targetIdx < 0 || targetIdx !== currentIdx && targetIdx !== currentIdx + 1) {
|
|
282
288
|
const next = getNextStatus(order.kind, order.status);
|
|
283
289
|
throw new Error(
|
|
284
290
|
`Invalid status transition: ${order.status} \u2192 ${input.status}. ` + (next ? `Expected next status: ${next}` : "Order is already in a terminal state.")
|
|
@@ -411,7 +417,349 @@ Cancelled: ${reason}` : `Cancelled: ${reason}`;
|
|
|
411
417
|
};
|
|
412
418
|
return provider;
|
|
413
419
|
}
|
|
414
|
-
|
|
420
|
+
var READ_ONLY = "This provider reads the core order archetype; pass `writes` to let the app that owns the flow create and edit them.";
|
|
421
|
+
var DEFAULT_CHANNELS = {
|
|
422
|
+
dine_in: "dine_in",
|
|
423
|
+
delivery: "delivery",
|
|
424
|
+
takeout: "balcao"
|
|
425
|
+
};
|
|
426
|
+
function createCoreOrdersProvider(options) {
|
|
427
|
+
const db = () => {
|
|
428
|
+
const client = getSupabaseClientOptional();
|
|
429
|
+
if (!client) throw new Error("Supabase client is not configured");
|
|
430
|
+
return client;
|
|
431
|
+
};
|
|
432
|
+
const writes = options?.writes;
|
|
433
|
+
function requireWrites() {
|
|
434
|
+
if (!writes) throw new Error(READ_ONLY);
|
|
435
|
+
return writes;
|
|
436
|
+
}
|
|
437
|
+
async function load(query, only) {
|
|
438
|
+
const client = db();
|
|
439
|
+
let q = client.from("v_orders").select("id, tenant_id, unit_id, kind, channel, status, reference_number, party_id, assignee_id, subtotal, discount, tax, total, currency, notes, created_at, updated_at, completed_at, fulfillment_status, metadata", { count: "exact" }).order("created_at", { ascending: false });
|
|
440
|
+
if (only?.id) q = q.eq("id", only.id);
|
|
441
|
+
if (options?.kinds?.length) q = q.in("kind", options.kinds);
|
|
442
|
+
if (options?.channel) q = q.eq("channel", options.channel);
|
|
443
|
+
if (query?.kind) {
|
|
444
|
+
q = Array.isArray(query.kind) ? q.in("kind", query.kind) : q.eq("kind", query.kind);
|
|
445
|
+
}
|
|
446
|
+
if (query?.status) {
|
|
447
|
+
q = Array.isArray(query.status) ? q.in("status", query.status) : q.eq("status", query.status);
|
|
448
|
+
}
|
|
449
|
+
if (query?.tableId) q = q.eq("metadata->>tableId", query.tableId);
|
|
450
|
+
if (query?.assigneeId) q = q.eq("assignee_id", query.assigneeId);
|
|
451
|
+
if (query?.dateRange) {
|
|
452
|
+
q = q.gte("created_at", query.dateRange.from).lte("created_at", query.dateRange.to);
|
|
453
|
+
}
|
|
454
|
+
const pageSize = query?.pageSize ?? 100;
|
|
455
|
+
const from = ((query?.page ?? 1) - 1) * pageSize;
|
|
456
|
+
q = q.range(from, from + pageSize - 1);
|
|
457
|
+
const { data, error, count } = await q;
|
|
458
|
+
if (error) throw new Error(error.message);
|
|
459
|
+
const rows = data ?? [];
|
|
460
|
+
const ids = rows.map((r) => r.id);
|
|
461
|
+
const [{ data: people }, { data: payments }, { data: items }] = await Promise.all([
|
|
462
|
+
ids.length ? client.from("people").select("id, name").in("id", [...new Set(rows.flatMap((r) => [r.party_id, r.assignee_id]).filter(Boolean))]) : Promise.resolve({ data: [] }),
|
|
463
|
+
ids.length ? client.from("v_order_payment_status").select("order_id, status, total_amount, paid_amount").in("order_id", ids) : Promise.resolve({ data: [] }),
|
|
464
|
+
ids.length ? client.from("items").select("id, order_id, product_id, name, description, quantity, unit_price, discount, total, sort_order, metadata, created_at").in("order_id", ids).order("sort_order") : Promise.resolve({ data: [] })
|
|
465
|
+
]);
|
|
466
|
+
const nameById = new Map((people ?? []).map((p) => [p.id, p.name]));
|
|
467
|
+
const payById = new Map((payments ?? []).map((p) => [p.order_id, p]));
|
|
468
|
+
const itemsByOrder = /* @__PURE__ */ new Map();
|
|
469
|
+
for (const item of items ?? []) {
|
|
470
|
+
const list = itemsByOrder.get(item.order_id) ?? [];
|
|
471
|
+
list.push(toItem(item));
|
|
472
|
+
itemsByOrder.set(item.order_id, list);
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
data: rows.map((r) => {
|
|
476
|
+
const pay = payById.get(r.id);
|
|
477
|
+
const meta = r.metadata ?? {};
|
|
478
|
+
return {
|
|
479
|
+
id: r.id,
|
|
480
|
+
referenceNumber: r.reference_number ?? String(r.id).slice(0, 8),
|
|
481
|
+
kind: r.kind,
|
|
482
|
+
status: r.status,
|
|
483
|
+
partyId: r.party_id ?? void 0,
|
|
484
|
+
partyName: r.party_id ? nameById.get(r.party_id) : void 0,
|
|
485
|
+
assigneeId: r.assignee_id ?? void 0,
|
|
486
|
+
assigneeName: r.assignee_id ? nameById.get(r.assignee_id) : void 0,
|
|
487
|
+
subtotal: Number(r.subtotal) || 0,
|
|
488
|
+
discount: Number(r.discount) || 0,
|
|
489
|
+
tax: Number(r.tax) || 0,
|
|
490
|
+
total: Number(r.total) || 0,
|
|
491
|
+
currency: r.currency ?? "BRL",
|
|
492
|
+
notes: r.notes ?? void 0,
|
|
493
|
+
tags: [],
|
|
494
|
+
// `source` is the archetype's channel where it has one; the salon's
|
|
495
|
+
// counter sale has none, and 'balcao' is what that means.
|
|
496
|
+
source: r.channel ?? "balcao",
|
|
497
|
+
// NOT location_id: that column is a FK to `app.units`, i.e. the
|
|
498
|
+
// BRANCH this order belongs to. A mesa has no table of its own yet,
|
|
499
|
+
// so its id rides in metadata until the floor plan gets one.
|
|
500
|
+
tableId: typeof meta.tableId === "string" ? meta.tableId : void 0,
|
|
501
|
+
estimatedMinutes: typeof meta.estimatedMinutes === "number" ? meta.estimatedMinutes : void 0,
|
|
502
|
+
items: itemsByOrder.get(r.id) ?? [],
|
|
503
|
+
metadata: {
|
|
504
|
+
...meta,
|
|
505
|
+
channel: r.channel ?? null,
|
|
506
|
+
fulfillmentStatus: r.fulfillment_status ?? null,
|
|
507
|
+
paymentStatus: pay?.status ?? "none",
|
|
508
|
+
invoicedAmount: Number(pay?.total_amount) || 0,
|
|
509
|
+
paidAmount: Number(pay?.paid_amount) || 0
|
|
510
|
+
},
|
|
511
|
+
tenantId: r.tenant_id,
|
|
512
|
+
unitId: r.unit_id ?? void 0,
|
|
513
|
+
createdAt: r.created_at,
|
|
514
|
+
updatedAt: r.updated_at,
|
|
515
|
+
completedAt: r.completed_at ?? void 0
|
|
516
|
+
};
|
|
517
|
+
}),
|
|
518
|
+
total: count ?? rows.length
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function toItem(row) {
|
|
522
|
+
const meta = row.metadata ?? {};
|
|
523
|
+
return {
|
|
524
|
+
id: row.id,
|
|
525
|
+
orderId: row.order_id,
|
|
526
|
+
productId: row.product_id ?? void 0,
|
|
527
|
+
name: row.name ?? "",
|
|
528
|
+
description: row.description ?? void 0,
|
|
529
|
+
quantity: Number(row.quantity) || 0,
|
|
530
|
+
unitPrice: Number(row.unit_price) || 0,
|
|
531
|
+
discount: Number(row.discount) || 0,
|
|
532
|
+
total: Number(row.total) || 0,
|
|
533
|
+
sortOrder: row.sort_order ?? 0,
|
|
534
|
+
modifiers: typeof meta.modifiers === "string" ? meta.modifiers : void 0,
|
|
535
|
+
kitchenNotes: typeof meta.kitchenNotes === "string" ? meta.kitchenNotes : void 0,
|
|
536
|
+
metadata: meta,
|
|
537
|
+
createdAt: row.created_at
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
function lineTotal(item) {
|
|
541
|
+
return item.quantity * item.unitPrice - (item.discount ?? 0);
|
|
542
|
+
}
|
|
543
|
+
async function loadOne(id) {
|
|
544
|
+
const page = await load(void 0, { id });
|
|
545
|
+
return page.data[0] ?? null;
|
|
546
|
+
}
|
|
547
|
+
async function recalc(orderId) {
|
|
548
|
+
const client = db();
|
|
549
|
+
const { data, error } = await client.from("items").select("total").eq("order_id", orderId);
|
|
550
|
+
if (error) throw new Error(error.message);
|
|
551
|
+
const subtotal = (data ?? []).reduce((sum, r) => sum + (Number(r.total) || 0), 0);
|
|
552
|
+
const { error: upErr } = await client.from("orders").update({ subtotal, total: subtotal }).eq("id", orderId);
|
|
553
|
+
if (upErr) throw new Error(upErr.message);
|
|
554
|
+
}
|
|
555
|
+
async function orderIdOfItem(itemId) {
|
|
556
|
+
const client = db();
|
|
557
|
+
const { data, error } = await client.from("items").select("order_id").eq("id", itemId).single();
|
|
558
|
+
if (error) throw new Error(error.message);
|
|
559
|
+
return data.order_id;
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
getOrders: (query) => load(query),
|
|
563
|
+
getOrderById: loadOne,
|
|
564
|
+
async createOrder(input) {
|
|
565
|
+
const w = requireWrites();
|
|
566
|
+
const client = db();
|
|
567
|
+
const tenantId = w.tenantId();
|
|
568
|
+
if (!tenantId) throw new Error("No active organization: an order cannot be opened without one");
|
|
569
|
+
const channel = (w.channelFor ?? ((k) => DEFAULT_CHANNELS[k]))(input.kind);
|
|
570
|
+
const subtotal = input.items.reduce(
|
|
571
|
+
(sum, i) => sum + lineTotal({ quantity: i.quantity, unitPrice: i.unitPrice, discount: i.discount }),
|
|
572
|
+
0
|
|
573
|
+
);
|
|
574
|
+
const { data: order, error } = await client.from("orders").insert({
|
|
575
|
+
tenant_id: tenantId,
|
|
576
|
+
unit_id: w.unitId?.() ?? null,
|
|
577
|
+
kind: input.kind,
|
|
578
|
+
status: "new",
|
|
579
|
+
...channel ? { channel } : {},
|
|
580
|
+
// party_id is left out on purpose: `trg_orders_require_party` fills it
|
|
581
|
+
// with `public.walkin_party(tenant_id)`, which is exactly what a table
|
|
582
|
+
// with no named customer is.
|
|
583
|
+
...input.partyId ? { party_id: input.partyId } : {},
|
|
584
|
+
...input.assigneeId ? { assignee_id: input.assigneeId } : {},
|
|
585
|
+
...input.notes ? { notes: input.notes } : {},
|
|
586
|
+
subtotal,
|
|
587
|
+
total: subtotal,
|
|
588
|
+
currency: "BRL",
|
|
589
|
+
metadata: {
|
|
590
|
+
...input.tableId ? { tableId: input.tableId } : {},
|
|
591
|
+
...input.estimatedMinutes ? { estimatedMinutes: input.estimatedMinutes } : {}
|
|
592
|
+
}
|
|
593
|
+
}).select("id").single();
|
|
594
|
+
if (error) throw new Error(error.message);
|
|
595
|
+
if (input.items.length) {
|
|
596
|
+
const { error: itemsErr } = await client.from("items").insert(
|
|
597
|
+
input.items.map((item, index) => ({
|
|
598
|
+
order_id: order.id,
|
|
599
|
+
// tenant_id and parent_kind are inherited from the parent by
|
|
600
|
+
// `trg_items_inherit`; setting them here would only duplicate it.
|
|
601
|
+
product_id: item.productId ?? null,
|
|
602
|
+
name: item.name,
|
|
603
|
+
quantity: item.quantity,
|
|
604
|
+
unit_price: item.unitPrice,
|
|
605
|
+
discount: item.discount ?? 0,
|
|
606
|
+
total: lineTotal(item),
|
|
607
|
+
sort_order: index,
|
|
608
|
+
metadata: {
|
|
609
|
+
...item.modifiers ? { modifiers: item.modifiers } : {},
|
|
610
|
+
...item.kitchenNotes ? { kitchenNotes: item.kitchenNotes } : {}
|
|
611
|
+
}
|
|
612
|
+
}))
|
|
613
|
+
);
|
|
614
|
+
if (itemsErr) throw new Error(itemsErr.message);
|
|
615
|
+
}
|
|
616
|
+
const created = await loadOne(order.id);
|
|
617
|
+
if (!created) throw new Error("The order was created but is not readable \u2014 check the orders.read permission");
|
|
618
|
+
return created;
|
|
619
|
+
},
|
|
620
|
+
async updateOrderStatus(input) {
|
|
621
|
+
requireWrites();
|
|
622
|
+
const client = db();
|
|
623
|
+
const { error } = await client.from("orders").update({ status: input.status }).eq("id", input.orderId);
|
|
624
|
+
if (error) throw new Error(error.message);
|
|
625
|
+
const order = await loadOne(input.orderId);
|
|
626
|
+
if (!order) throw new Error("Order not found after the status change");
|
|
627
|
+
return order;
|
|
628
|
+
},
|
|
629
|
+
async updateOrder(id, data) {
|
|
630
|
+
requireWrites();
|
|
631
|
+
const client = db();
|
|
632
|
+
const patch = {};
|
|
633
|
+
if (data.status !== void 0) patch.status = data.status;
|
|
634
|
+
if (data.notes !== void 0) patch.notes = data.notes;
|
|
635
|
+
if (data.partyId !== void 0) patch.party_id = data.partyId;
|
|
636
|
+
if (data.assigneeId !== void 0) patch.assignee_id = data.assigneeId;
|
|
637
|
+
if (data.discount !== void 0) patch.discount = data.discount;
|
|
638
|
+
if (data.tax !== void 0) patch.tax = data.tax;
|
|
639
|
+
if (Object.keys(patch).length) {
|
|
640
|
+
const { error } = await client.from("orders").update(patch).eq("id", id);
|
|
641
|
+
if (error) throw new Error(error.message);
|
|
642
|
+
}
|
|
643
|
+
const order = await loadOne(id);
|
|
644
|
+
if (!order) throw new Error("Order not found after the update");
|
|
645
|
+
return order;
|
|
646
|
+
},
|
|
647
|
+
async cancelOrder(id, reason) {
|
|
648
|
+
requireWrites();
|
|
649
|
+
const client = db();
|
|
650
|
+
const { error } = await client.from("orders").update({ status: "cancelled", ...reason ? { notes: reason } : {} }).eq("id", id);
|
|
651
|
+
if (error) throw new Error(error.message);
|
|
652
|
+
},
|
|
653
|
+
async addOrderItem(orderId, item) {
|
|
654
|
+
requireWrites();
|
|
655
|
+
const client = db();
|
|
656
|
+
const { count } = await client.from("items").select("id", { count: "exact", head: true }).eq("order_id", orderId);
|
|
657
|
+
const { data, error } = await client.from("items").insert({
|
|
658
|
+
order_id: orderId,
|
|
659
|
+
product_id: item.productId ?? null,
|
|
660
|
+
name: item.name,
|
|
661
|
+
quantity: item.quantity,
|
|
662
|
+
unit_price: item.unitPrice,
|
|
663
|
+
discount: item.discount ?? 0,
|
|
664
|
+
total: lineTotal(item),
|
|
665
|
+
sort_order: count ?? 0,
|
|
666
|
+
metadata: {
|
|
667
|
+
...item.modifiers ? { modifiers: item.modifiers } : {},
|
|
668
|
+
...item.kitchenNotes ? { kitchenNotes: item.kitchenNotes } : {}
|
|
669
|
+
}
|
|
670
|
+
}).select("id, order_id, product_id, name, description, quantity, unit_price, discount, total, sort_order, metadata, created_at").single();
|
|
671
|
+
if (error) throw new Error(error.message);
|
|
672
|
+
await recalc(orderId);
|
|
673
|
+
return toItem(data);
|
|
674
|
+
},
|
|
675
|
+
async removeOrderItem(itemId) {
|
|
676
|
+
requireWrites();
|
|
677
|
+
const client = db();
|
|
678
|
+
const orderId = await orderIdOfItem(itemId);
|
|
679
|
+
const { error } = await client.from("items").delete().eq("id", itemId);
|
|
680
|
+
if (error) throw new Error(error.message);
|
|
681
|
+
await recalc(orderId);
|
|
682
|
+
},
|
|
683
|
+
async updateOrderItem(itemId, data) {
|
|
684
|
+
requireWrites();
|
|
685
|
+
const client = db();
|
|
686
|
+
const orderId = await orderIdOfItem(itemId);
|
|
687
|
+
const patch = {};
|
|
688
|
+
if (data.name !== void 0) patch.name = data.name;
|
|
689
|
+
if (data.quantity !== void 0) patch.quantity = data.quantity;
|
|
690
|
+
if (data.unitPrice !== void 0) patch.unit_price = data.unitPrice;
|
|
691
|
+
if (data.discount !== void 0) patch.discount = data.discount;
|
|
692
|
+
if (data.quantity !== void 0 || data.unitPrice !== void 0 || data.discount !== void 0) {
|
|
693
|
+
const { data: current, error: readErr } = await client.from("items").select("quantity, unit_price, discount").eq("id", itemId).single();
|
|
694
|
+
if (readErr) throw new Error(readErr.message);
|
|
695
|
+
patch.total = lineTotal({
|
|
696
|
+
quantity: Number(data.quantity ?? current.quantity) || 0,
|
|
697
|
+
unitPrice: Number(data.unitPrice ?? current.unit_price) || 0,
|
|
698
|
+
discount: Number(data.discount ?? current.discount) || 0
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
const { data: row, error } = await client.from("items").update(patch).eq("id", itemId).select("id, order_id, product_id, name, description, quantity, unit_price, discount, total, sort_order, metadata, created_at").single();
|
|
702
|
+
if (error) throw new Error(error.message);
|
|
703
|
+
await recalc(orderId);
|
|
704
|
+
return toItem(row);
|
|
705
|
+
},
|
|
706
|
+
async getSummary(dateRange) {
|
|
707
|
+
const page = await load({ pageSize: 500, ...dateRange ? { dateRange } : {} });
|
|
708
|
+
const live = page.data.filter((o) => o.status !== "cancelled");
|
|
709
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
710
|
+
const todays = live.filter((o) => o.createdAt.slice(0, 10) === today);
|
|
711
|
+
const revenue = todays.reduce((sum, o) => sum + o.total, 0);
|
|
712
|
+
const byKind = {};
|
|
713
|
+
const byStatus = {};
|
|
714
|
+
for (const o of live) {
|
|
715
|
+
byKind[o.kind] = (byKind[o.kind] ?? 0) + 1;
|
|
716
|
+
byStatus[o.status] = (byStatus[o.status] ?? 0) + 1;
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
todayOrderCount: todays.length,
|
|
720
|
+
todayRevenue: revenue,
|
|
721
|
+
averageTicket: todays.length ? revenue / todays.length : 0,
|
|
722
|
+
activeOrderCount: live.filter((o) => o.status !== "completed").length,
|
|
723
|
+
ordersByKind: byKind,
|
|
724
|
+
ordersByStatus: byStatus,
|
|
725
|
+
topItems: []
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
var ACTIVE_STATUSES = [
|
|
731
|
+
"new",
|
|
732
|
+
"confirmed",
|
|
733
|
+
"preparing",
|
|
734
|
+
"ready",
|
|
735
|
+
// The `Saiu` column: one word per kind for the same moment.
|
|
736
|
+
"served",
|
|
737
|
+
"picked_up",
|
|
738
|
+
"dispatched",
|
|
739
|
+
"delivered"
|
|
740
|
+
];
|
|
741
|
+
function emitOrderLifecycleEvent(key, order) {
|
|
742
|
+
emitDomainEvent(key, {
|
|
743
|
+
order_id: order.id,
|
|
744
|
+
reference_number: order.referenceNumber,
|
|
745
|
+
total: order.total,
|
|
746
|
+
item_count: order.items.length,
|
|
747
|
+
source: order.source
|
|
748
|
+
}, {
|
|
749
|
+
tenantId: order.tenantId,
|
|
750
|
+
subject: { type: "order", id: order.id }
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
async function runLifecycleHook(label, order, hook) {
|
|
754
|
+
if (!hook) return;
|
|
755
|
+
try {
|
|
756
|
+
await hook(order);
|
|
757
|
+
} catch (error) {
|
|
758
|
+
const message = errorMessage(error);
|
|
759
|
+
throw new Error(`Order was ${label}, but downstream synchronization is pending: ${message}`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
function createOrdersStore(provider, hooks = {}) {
|
|
415
763
|
return createStore((set, get) => ({
|
|
416
764
|
orders: [],
|
|
417
765
|
ordersTotal: 0,
|
|
@@ -432,9 +780,7 @@ function createOrdersStore(provider) {
|
|
|
432
780
|
async fetchActiveOrders() {
|
|
433
781
|
return dedup("orders:active", async () => {
|
|
434
782
|
set({ activeOrdersLoading: true });
|
|
435
|
-
const result = await provider.getOrders({
|
|
436
|
-
status: ["new", "confirmed", "preparing", "ready"]
|
|
437
|
-
});
|
|
783
|
+
const result = await provider.getOrders({ status: [...ACTIVE_STATUSES] });
|
|
438
784
|
set({ activeOrders: result.data, activeOrdersLoading: false });
|
|
439
785
|
});
|
|
440
786
|
},
|
|
@@ -457,10 +803,10 @@ function createOrdersStore(provider) {
|
|
|
457
803
|
provider.getSummary()
|
|
458
804
|
]);
|
|
459
805
|
set({ orders: result.data, ordersTotal: result.total, summary });
|
|
460
|
-
toast.success("
|
|
806
|
+
toast.success("Pedido criado");
|
|
461
807
|
return order;
|
|
462
808
|
} catch (err) {
|
|
463
|
-
toast.error("
|
|
809
|
+
toast.error("N\xE3o foi poss\xEDvel criar o pedido", { description: err?.message });
|
|
464
810
|
throw err;
|
|
465
811
|
}
|
|
466
812
|
},
|
|
@@ -471,44 +817,58 @@ function createOrdersStore(provider) {
|
|
|
471
817
|
if (!order) throw new Error("Order not found");
|
|
472
818
|
const nextStatus = getNextStatus(order.kind, order.status);
|
|
473
819
|
if (!nextStatus) throw new Error("No next status available");
|
|
474
|
-
await provider.updateOrderStatus({ orderId, status: nextStatus });
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
820
|
+
const updated = await provider.updateOrderStatus({ orderId, status: nextStatus });
|
|
821
|
+
if (updated.status === "completed") {
|
|
822
|
+
emitOrderLifecycleEvent("orders.order_completed", updated);
|
|
823
|
+
await runLifecycleHook("completed", updated, hooks.onOrderCompleted);
|
|
824
|
+
}
|
|
825
|
+
const activeResult = await provider.getOrders({ status: [...ACTIVE_STATUSES] });
|
|
478
826
|
set({ activeOrders: activeResult.data });
|
|
479
|
-
toast.success(
|
|
827
|
+
toast.success("Pedido avan\xE7ado");
|
|
480
828
|
} catch (err) {
|
|
481
|
-
toast.error("
|
|
829
|
+
toast.error("N\xE3o foi poss\xEDvel avan\xE7ar o pedido", { description: err?.message });
|
|
482
830
|
throw err;
|
|
483
831
|
}
|
|
484
832
|
},
|
|
485
833
|
async updateOrderStatus(input) {
|
|
486
834
|
try {
|
|
487
|
-
await provider.
|
|
835
|
+
const before = await provider.getOrderById(input.orderId);
|
|
836
|
+
const updated = await provider.updateOrderStatus(input);
|
|
837
|
+
if (updated.status === "completed") {
|
|
838
|
+
if (before?.status !== "completed") emitOrderLifecycleEvent("orders.order_completed", updated);
|
|
839
|
+
await runLifecycleHook("completed", updated, hooks.onOrderCompleted);
|
|
840
|
+
}
|
|
488
841
|
const query = get().orderQuery;
|
|
489
842
|
const [result, summary] = await Promise.all([
|
|
490
843
|
provider.getOrders(query),
|
|
491
844
|
provider.getSummary()
|
|
492
845
|
]);
|
|
493
846
|
set({ orders: result.data, ordersTotal: result.total, summary });
|
|
494
|
-
toast.success("
|
|
847
|
+
toast.success("Status atualizado");
|
|
495
848
|
} catch (err) {
|
|
496
|
-
toast.error("
|
|
849
|
+
toast.error("N\xE3o foi poss\xEDvel atualizar o status", { description: err?.message });
|
|
497
850
|
throw err;
|
|
498
851
|
}
|
|
499
852
|
},
|
|
500
853
|
async cancelOrder(id, reason) {
|
|
501
854
|
try {
|
|
855
|
+
const before = await provider.getOrderById(id);
|
|
502
856
|
await provider.cancelOrder(id, reason);
|
|
857
|
+
const cancelled = await provider.getOrderById(id);
|
|
858
|
+
const order = cancelled ?? (before ? { ...before, status: "cancelled" } : null);
|
|
859
|
+
if (order) {
|
|
860
|
+
if (before?.status !== "cancelled") emitOrderLifecycleEvent("orders.order_cancelled", order);
|
|
861
|
+
await runLifecycleHook("cancelled", order, hooks.onOrderCancelled);
|
|
862
|
+
}
|
|
503
863
|
const query = get().orderQuery;
|
|
504
864
|
const [result, summary] = await Promise.all([
|
|
505
865
|
provider.getOrders(query),
|
|
506
866
|
provider.getSummary()
|
|
507
867
|
]);
|
|
508
868
|
set({ orders: result.data, ordersTotal: result.total, summary });
|
|
509
|
-
toast.success("
|
|
869
|
+
toast.success("Pedido cancelado");
|
|
510
870
|
} catch (err) {
|
|
511
|
-
toast.error("
|
|
871
|
+
toast.error("N\xE3o foi poss\xEDvel cancelar o pedido", { description: err?.message });
|
|
512
872
|
throw err;
|
|
513
873
|
}
|
|
514
874
|
},
|
|
@@ -518,9 +878,9 @@ function createOrdersStore(provider) {
|
|
|
518
878
|
const query = get().orderQuery;
|
|
519
879
|
const result = await provider.getOrders(query);
|
|
520
880
|
set({ orders: result.data, ordersTotal: result.total });
|
|
521
|
-
toast.success("Item
|
|
881
|
+
toast.success("Item adicionado");
|
|
522
882
|
} catch (err) {
|
|
523
|
-
toast.error("
|
|
883
|
+
toast.error("N\xE3o foi poss\xEDvel adicionar o item", { description: err?.message });
|
|
524
884
|
throw err;
|
|
525
885
|
}
|
|
526
886
|
},
|
|
@@ -530,9 +890,9 @@ function createOrdersStore(provider) {
|
|
|
530
890
|
const query = get().orderQuery;
|
|
531
891
|
const result = await provider.getOrders(query);
|
|
532
892
|
set({ orders: result.data, ordersTotal: result.total });
|
|
533
|
-
toast.success("Item
|
|
893
|
+
toast.success("Item removido");
|
|
534
894
|
} catch (err) {
|
|
535
|
-
toast.error("
|
|
895
|
+
toast.error("N\xE3o foi poss\xEDvel remover o item", { description: err?.message });
|
|
536
896
|
throw err;
|
|
537
897
|
}
|
|
538
898
|
}
|
|
@@ -572,22 +932,47 @@ var ordersRegistries = [
|
|
|
572
932
|
// src/locales/en.ts
|
|
573
933
|
var en = {
|
|
574
934
|
"orders.title": "Orders",
|
|
935
|
+
"orders.detail.addItem": "Add item",
|
|
936
|
+
"orders.detail.searchMenu": "Search the menu\u2026",
|
|
937
|
+
"orders.detail.cancel": "Cancel order",
|
|
938
|
+
"orders.detail.cancelConfirm": "Cancel {reference}? This cannot be undone.",
|
|
575
939
|
"orders.nav.kanban": "Orders",
|
|
576
940
|
"orders.nav.list": "History",
|
|
577
941
|
"orders.nav.new": "New Order",
|
|
578
942
|
"orders.nav.dashboard": "Dashboard",
|
|
943
|
+
"orders.list.column.order": "Order",
|
|
944
|
+
"orders.list.column.customer": "Customer",
|
|
945
|
+
"orders.list.column.total": "Total",
|
|
946
|
+
"orders.list.column.status": "Status",
|
|
947
|
+
"orders.list.searchPlaceholder": "Search by number, customer or table\u2026",
|
|
579
948
|
"orders.kanban.title": "Orders",
|
|
580
949
|
"orders.kanban.subtitle": "Active orders",
|
|
581
950
|
"orders.kanban.noOrders": "No active orders",
|
|
582
951
|
"orders.kanban.noOrdersDesc": "Orders will appear here as they come in",
|
|
583
952
|
"orders.kanban.newOrder": "New Order",
|
|
584
953
|
"orders.kanban.advance": "Advance",
|
|
954
|
+
// O verbo de cada passo, chaveado pelo estado ALVO. E o que o botao do
|
|
955
|
+
// cartao diz: quem esta no salao nao "avanca" um pedido, ele confirma,
|
|
956
|
+
// prepara, serve ou despacha.
|
|
957
|
+
"orders.advance.draft": "Save draft",
|
|
958
|
+
"orders.advance.new": "Send",
|
|
959
|
+
"orders.advance.confirmed": "Confirm",
|
|
960
|
+
"orders.advance.preparing": "Start prep",
|
|
961
|
+
"orders.advance.ready": "Mark ready",
|
|
962
|
+
"orders.advance.served": "Serve",
|
|
963
|
+
"orders.advance.picked_up": "Hand over",
|
|
964
|
+
"orders.advance.dispatched": "Dispatch",
|
|
965
|
+
"orders.advance.delivered": "Confirm delivery",
|
|
966
|
+
"orders.advance.completed": "Complete",
|
|
967
|
+
"orders.advance.cancelled": "Cancel",
|
|
585
968
|
"orders.kanban.cancel": "Cancel Order",
|
|
586
969
|
"orders.kanban.column.new": "New",
|
|
587
970
|
"orders.kanban.column.confirmed": "Confirmed",
|
|
971
|
+
"orders.kanban.column.open": "Open",
|
|
588
972
|
"orders.kanban.column.preparing": "Preparing",
|
|
589
973
|
"orders.kanban.column.ready": "Ready",
|
|
590
974
|
"orders.kanban.column.out": "Out",
|
|
975
|
+
"orders.kanban.column.served": "Served",
|
|
591
976
|
"orders.kanban.column.completed": "Completed",
|
|
592
977
|
"orders.kanban.column.cancelled": "Cancelled",
|
|
593
978
|
"orders.detail.title": "Order Details",
|
|
@@ -627,22 +1012,47 @@ var en = {
|
|
|
627
1012
|
// src/locales/pt-BR.ts
|
|
628
1013
|
var ptBR = {
|
|
629
1014
|
"orders.title": "Pedidos",
|
|
1015
|
+
"orders.detail.addItem": "Lan\xE7ar item",
|
|
1016
|
+
"orders.detail.searchMenu": "Buscar no card\xE1pio\u2026",
|
|
1017
|
+
"orders.detail.cancel": "Cancelar pedido",
|
|
1018
|
+
"orders.detail.cancelConfirm": "Cancelar {reference}? N\xE3o d\xE1 para desfazer.",
|
|
630
1019
|
"orders.nav.kanban": "Pedidos",
|
|
631
1020
|
"orders.nav.list": "Hist\xF3rico",
|
|
632
1021
|
"orders.nav.new": "Novo Pedido",
|
|
633
1022
|
"orders.nav.dashboard": "Painel",
|
|
1023
|
+
"orders.list.column.order": "Pedido",
|
|
1024
|
+
"orders.list.column.customer": "Cliente",
|
|
1025
|
+
"orders.list.column.total": "Total",
|
|
1026
|
+
"orders.list.column.status": "Estado",
|
|
1027
|
+
"orders.list.searchPlaceholder": "Buscar por n\xFAmero, cliente ou mesa\u2026",
|
|
634
1028
|
"orders.kanban.title": "Pedidos",
|
|
635
1029
|
"orders.kanban.subtitle": "Pedidos ativos",
|
|
636
1030
|
"orders.kanban.noOrders": "Nenhum pedido ativo",
|
|
637
1031
|
"orders.kanban.noOrdersDesc": "Os pedidos aparecer\xE3o aqui conforme chegarem",
|
|
638
1032
|
"orders.kanban.newOrder": "Novo Pedido",
|
|
639
1033
|
"orders.kanban.advance": "Avan\xE7ar",
|
|
1034
|
+
// O verbo de cada passo, chaveado pelo estado ALVO. E o que o botao do
|
|
1035
|
+
// cartao diz: quem esta no salao nao "avanca" um pedido, ele confirma,
|
|
1036
|
+
// prepara, serve ou despacha.
|
|
1037
|
+
"orders.advance.draft": "Salvar rascunho",
|
|
1038
|
+
"orders.advance.new": "Enviar",
|
|
1039
|
+
"orders.advance.confirmed": "Confirmar",
|
|
1040
|
+
"orders.advance.preparing": "Preparar",
|
|
1041
|
+
"orders.advance.ready": "Marcar pronto",
|
|
1042
|
+
"orders.advance.served": "Servir",
|
|
1043
|
+
"orders.advance.picked_up": "Entregar",
|
|
1044
|
+
"orders.advance.dispatched": "Despachar",
|
|
1045
|
+
"orders.advance.delivered": "Confirmar entrega",
|
|
1046
|
+
"orders.advance.completed": "Concluir",
|
|
1047
|
+
"orders.advance.cancelled": "Cancelar",
|
|
640
1048
|
"orders.kanban.cancel": "Cancelar Pedido",
|
|
641
1049
|
"orders.kanban.column.new": "Novo",
|
|
642
1050
|
"orders.kanban.column.confirmed": "Confirmado",
|
|
1051
|
+
"orders.kanban.column.open": "Aberta",
|
|
643
1052
|
"orders.kanban.column.preparing": "Preparando",
|
|
644
1053
|
"orders.kanban.column.ready": "Pronto",
|
|
645
1054
|
"orders.kanban.column.out": "Saiu",
|
|
1055
|
+
"orders.kanban.column.served": "Servido",
|
|
646
1056
|
"orders.kanban.column.completed": "Conclu\xEDdo",
|
|
647
1057
|
"orders.kanban.column.cancelled": "Cancelado",
|
|
648
1058
|
"orders.detail.title": "Detalhes do Pedido",
|
|
@@ -1128,10 +1538,88 @@ var aiqfomeConnectorDef = {
|
|
|
1128
1538
|
disconnect: () => store6.disconnect(),
|
|
1129
1539
|
disconnectNote: "Desconectar para de trazer pedidos novos. Os pedidos que j\xE1 entraram continuam aqui."
|
|
1130
1540
|
};
|
|
1541
|
+
function toModalInstallment(bill) {
|
|
1542
|
+
return {
|
|
1543
|
+
id: bill.id,
|
|
1544
|
+
amount: Number(bill.amount ?? 0),
|
|
1545
|
+
paidAmount: Number(bill.paid_amount ?? 0),
|
|
1546
|
+
installmentNumber: bill.installment_number ?? 1,
|
|
1547
|
+
direction: bill.direction ?? "credit",
|
|
1548
|
+
dueDate: bill.due_date,
|
|
1549
|
+
invoiceId: bill.financial_invoice_id ?? bill.invoice_id
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
function firstOpenBill(receivable) {
|
|
1553
|
+
return receivable?.bills.find(
|
|
1554
|
+
(b) => Number(b.paid_amount ?? 0) < Number(b.amount)
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
function OrderReceivableActions({ orderId, orderTotal, receivable, reload }) {
|
|
1558
|
+
const [busy, setBusy] = React2.useState(false);
|
|
1559
|
+
const [paying, setPaying] = React2.useState(null);
|
|
1560
|
+
const can = usePermissionOptional();
|
|
1561
|
+
const runtime = usePluginRuntimeOptional();
|
|
1562
|
+
const hasFinancialRoute = (runtime?.activePlugins ?? []).some(
|
|
1563
|
+
(p) => p.routes?.some((r) => r.path === "/financial")
|
|
1564
|
+
);
|
|
1565
|
+
const canInvoice = can("financial", "create");
|
|
1566
|
+
const balance = receivable ? Number(receivable.balance.balance ?? 0) : 0;
|
|
1567
|
+
const cancelled = receivable?.balance.status === "cancelled";
|
|
1568
|
+
const openBill = firstOpenBill(receivable);
|
|
1569
|
+
const canReceive = receivable ? balance > 0 && !cancelled && Boolean(openBill) : canInvoice && Number(orderTotal) > 0;
|
|
1570
|
+
async function registrarRecebimento() {
|
|
1571
|
+
if (receivable) {
|
|
1572
|
+
const bill = firstOpenBill(receivable);
|
|
1573
|
+
if (bill) setPaying(toModalInstallment(bill));
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
const db = getSupabaseClientOptional();
|
|
1577
|
+
if (!db) return;
|
|
1578
|
+
setBusy(true);
|
|
1579
|
+
try {
|
|
1580
|
+
const { error } = await db.rpc("fn_invoice_from_order", { p_order_id: orderId });
|
|
1581
|
+
if (error) throw new Error(error.message ?? "Falha ao faturar o pedido");
|
|
1582
|
+
const { data } = await db.from("v_financial_bills").select("*").eq("invoice_id", orderId).order("installment_number", { ascending: true });
|
|
1583
|
+
const bill = (data ?? []).find(
|
|
1584
|
+
(b) => Number(b.paid_amount ?? 0) < Number(b.amount ?? 0)
|
|
1585
|
+
);
|
|
1586
|
+
await reload(true);
|
|
1587
|
+
if (bill) setPaying(toModalInstallment(bill));
|
|
1588
|
+
else toast$1.error("Pedido faturado, mas nenhuma parcela em aberto foi encontrada");
|
|
1589
|
+
} catch (err) {
|
|
1590
|
+
toast$1.error(err instanceof Error ? err.message : "Falha ao faturar o pedido");
|
|
1591
|
+
} finally {
|
|
1592
|
+
setBusy(false);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1596
|
+
canReceive && /* @__PURE__ */ jsx(Button, { onClick: () => void registrarRecebimento(), disabled: busy, "data-testid": "order-register-receipt", children: busy ? "Abrindo\u2026" : "Registrar recebimento" }),
|
|
1597
|
+
hasFinancialRoute && /* @__PURE__ */ jsx(
|
|
1598
|
+
"button",
|
|
1599
|
+
{
|
|
1600
|
+
type: "button",
|
|
1601
|
+
onClick: () => adminNavigateTo("/financial"),
|
|
1602
|
+
className: "rounded-md border px-3 py-1.5 text-sm font-medium transition hover:bg-muted",
|
|
1603
|
+
children: "Abrir no Financeiro"
|
|
1604
|
+
}
|
|
1605
|
+
),
|
|
1606
|
+
paying != null && /* @__PURE__ */ jsx(
|
|
1607
|
+
ReceivePaymentModal,
|
|
1608
|
+
{
|
|
1609
|
+
installment: paying,
|
|
1610
|
+
onClose: () => setPaying(null),
|
|
1611
|
+
onPaid: () => {
|
|
1612
|
+
setPaying(null);
|
|
1613
|
+
void reload(true);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
)
|
|
1617
|
+
] });
|
|
1618
|
+
}
|
|
1131
1619
|
var DEFAULT_ORDERS_TABLE = "orders";
|
|
1132
|
-
var DEFAULT_ORDER_ITEMS_TABLE = "
|
|
1620
|
+
var DEFAULT_ORDER_ITEMS_TABLE = "items";
|
|
1133
1621
|
var ORDERS_METADATA_KEY = "fayzOrders";
|
|
1134
|
-
var
|
|
1622
|
+
var ACTIVE_STATUSES2 = /* @__PURE__ */ new Set(["new", "confirmed", "preparing", "ready", "served", "dispatched"]);
|
|
1135
1623
|
var RESTAURANT_STATUSES = /* @__PURE__ */ new Set([
|
|
1136
1624
|
"new",
|
|
1137
1625
|
"confirmed",
|
|
@@ -1373,7 +1861,7 @@ function summarize(orders, dateRange) {
|
|
|
1373
1861
|
todayOrderCount: inRange.length,
|
|
1374
1862
|
todayRevenue: revenue,
|
|
1375
1863
|
averageTicket: billable.length > 0 ? revenue / billable.length : 0,
|
|
1376
|
-
activeOrderCount: orders.filter((order) =>
|
|
1864
|
+
activeOrderCount: orders.filter((order) => ACTIVE_STATUSES2.has(order.status)).length,
|
|
1377
1865
|
ordersByKind,
|
|
1378
1866
|
ordersByStatus,
|
|
1379
1867
|
topItems: [...top.values()].sort((a, b) => b.quantity - a.quantity).slice(0, 5)
|
|
@@ -1557,7 +2045,7 @@ function createFayzOrdersProvider(options = {}) {
|
|
|
1557
2045
|
}
|
|
1558
2046
|
|
|
1559
2047
|
// src/index.ts
|
|
1560
|
-
var OrdersPage =
|
|
2048
|
+
var OrdersPage = React2__default.lazy(() => import('./OrdersPage-IKIIQALX.js').then((m) => ({ default: m.OrdersPage })));
|
|
1561
2049
|
var DEFAULT_LABELS = {
|
|
1562
2050
|
pageTitle: "Orders",
|
|
1563
2051
|
kanban: "Orders",
|
|
@@ -1580,18 +2068,58 @@ function resolveConfig(options) {
|
|
|
1580
2068
|
orderSources: options?.orderSources ?? DEFAULT_ORDER_SOURCES,
|
|
1581
2069
|
menuItemLookup: options?.menuItemLookup,
|
|
1582
2070
|
staffLookup: options?.staffLookup,
|
|
1583
|
-
onOrderCompleted: options?.onOrderCompleted
|
|
2071
|
+
onOrderCompleted: options?.onOrderCompleted,
|
|
2072
|
+
onOrderCancelled: options?.onOrderCancelled,
|
|
2073
|
+
createRoute: options?.createRoute,
|
|
2074
|
+
editRoute: options?.editRoute,
|
|
2075
|
+
canEditOrder: options?.canEditOrder,
|
|
2076
|
+
orderRoute: options?.orderRoute
|
|
1584
2077
|
};
|
|
1585
2078
|
}
|
|
1586
2079
|
function createOrdersPlugin(options) {
|
|
1587
2080
|
const config = resolveConfig(options);
|
|
1588
|
-
const provider = options?.dataProvider ?? createMockOrdersProvider();
|
|
1589
|
-
const store7 = createOrdersStore(provider
|
|
1590
|
-
|
|
1591
|
-
|
|
2081
|
+
const provider = options?.dataProvider ?? (options?.variant === "commercial" ? createCoreOrdersProvider() : createMockOrdersProvider());
|
|
2082
|
+
const store7 = createOrdersStore(provider, {
|
|
2083
|
+
onOrderCompleted: config.onOrderCompleted,
|
|
2084
|
+
onOrderCancelled: config.onOrderCancelled
|
|
2085
|
+
});
|
|
2086
|
+
const PageComponent = () => React2__default.createElement(
|
|
2087
|
+
React2__default.Suspense,
|
|
1592
2088
|
{ fallback: null },
|
|
1593
|
-
|
|
2089
|
+
React2__default.createElement(OrdersPage, { config, provider, store: store7, registries: ordersRegistries, variant: options?.variant ?? "restaurant" })
|
|
1594
2090
|
);
|
|
2091
|
+
const OrderDetailRoute = ({ id }) => id ? React2__default.createElement(OrderDetailPage, {
|
|
2092
|
+
orderId: id,
|
|
2093
|
+
backTo: "/orders",
|
|
2094
|
+
// The header had no verbs at all — not just no "Editar". The page
|
|
2095
|
+
// always had the slot; nobody was filling it.
|
|
2096
|
+
actions: (order) => {
|
|
2097
|
+
const edit = options?.editRoute;
|
|
2098
|
+
if (!edit) return null;
|
|
2099
|
+
const allowed = options?.canEditOrder ?? orderIsEditable;
|
|
2100
|
+
if (!allowed(order)) return null;
|
|
2101
|
+
return React2__default.createElement(PageActions, {
|
|
2102
|
+
actions: [{
|
|
2103
|
+
id: "edit",
|
|
2104
|
+
testId: "order-edit",
|
|
2105
|
+
label: "Editar pedido",
|
|
2106
|
+
icon: Pencil,
|
|
2107
|
+
intent: "primary",
|
|
2108
|
+
onSelect: () => adminNavigateTo(edit(order.id))
|
|
2109
|
+
}]
|
|
2110
|
+
});
|
|
2111
|
+
},
|
|
2112
|
+
// Os verbos do dinheiro. O seam existia desde a primeira passada do
|
|
2113
|
+
// archetype e nunca foi preenchido fora da loja — entao a tela sabia
|
|
2114
|
+
// dizer "sem conta a receber" e nao oferecia como criar uma. Ver
|
|
2115
|
+
// ./views/OrderReceivableActions.
|
|
2116
|
+
receivableActions: (order, ctx) => React2__default.createElement(OrderReceivableActions, {
|
|
2117
|
+
orderId: order.id,
|
|
2118
|
+
orderTotal: order.total ?? 0,
|
|
2119
|
+
receivable: ctx.receivable,
|
|
2120
|
+
reload: ctx.reload
|
|
2121
|
+
})
|
|
2122
|
+
}) : null;
|
|
1595
2123
|
return {
|
|
1596
2124
|
id: "orders",
|
|
1597
2125
|
defaultAgentRole: "operations",
|
|
@@ -1602,6 +2130,40 @@ function createOrdersPlugin(options) {
|
|
|
1602
2130
|
verticalId: options?.verticalId,
|
|
1603
2131
|
defaultEnabled: true,
|
|
1604
2132
|
dependencies: ["menu"],
|
|
2133
|
+
events: [
|
|
2134
|
+
{
|
|
2135
|
+
name: "orders.order_completed",
|
|
2136
|
+
title: "Order completed",
|
|
2137
|
+
trigger: "When an order reaches the completed status.",
|
|
2138
|
+
payloadSchema: {
|
|
2139
|
+
type: "object",
|
|
2140
|
+
properties: {
|
|
2141
|
+
order_id: { type: "string" },
|
|
2142
|
+
reference_number: { type: "string" },
|
|
2143
|
+
total: { type: "number" },
|
|
2144
|
+
item_count: { type: "number" },
|
|
2145
|
+
source: { type: "string" }
|
|
2146
|
+
},
|
|
2147
|
+
required: ["order_id", "reference_number", "total", "item_count", "source"]
|
|
2148
|
+
}
|
|
2149
|
+
},
|
|
2150
|
+
{
|
|
2151
|
+
name: "orders.order_cancelled",
|
|
2152
|
+
title: "Order cancelled",
|
|
2153
|
+
trigger: "When an order reaches the cancelled status.",
|
|
2154
|
+
payloadSchema: {
|
|
2155
|
+
type: "object",
|
|
2156
|
+
properties: {
|
|
2157
|
+
order_id: { type: "string" },
|
|
2158
|
+
reference_number: { type: "string" },
|
|
2159
|
+
total: { type: "number" },
|
|
2160
|
+
item_count: { type: "number" },
|
|
2161
|
+
source: { type: "string" }
|
|
2162
|
+
},
|
|
2163
|
+
required: ["order_id", "reference_number", "total", "item_count", "source"]
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
],
|
|
1605
2167
|
// Orders/month quota. NOTE: order creation currently flows only through the
|
|
1606
2168
|
// store/AI-tool (createOrder) with no dedicated UI create handler, so no
|
|
1607
2169
|
// client-side guard is wired yet — the declaration keeps the cap countable.
|
|
@@ -1623,6 +2185,14 @@ function createOrdersPlugin(options) {
|
|
|
1623
2185
|
path: "/orders",
|
|
1624
2186
|
component: PageComponent,
|
|
1625
2187
|
permission: { feature: "orders", action: "read" }
|
|
2188
|
+
},
|
|
2189
|
+
{
|
|
2190
|
+
// The page the rail's "Ver pedido completo" points at. It used to exist
|
|
2191
|
+
// only in plugin-shop, so the restaurant's link opened a blank screen —
|
|
2192
|
+
// the route was never mounted, not the data missing.
|
|
2193
|
+
path: "/orders/:id",
|
|
2194
|
+
component: OrderDetailRoute,
|
|
2195
|
+
permission: { feature: "orders", action: "read" }
|
|
1626
2196
|
}
|
|
1627
2197
|
],
|
|
1628
2198
|
widgets: [],
|
|
@@ -1692,7 +2262,7 @@ function createOrdersPlugin(options) {
|
|
|
1692
2262
|
label: config.labels.pageTitle,
|
|
1693
2263
|
icon: "ClipboardList",
|
|
1694
2264
|
component: (() => {
|
|
1695
|
-
const OrdersSettingsTab = () =>
|
|
2265
|
+
const OrdersSettingsTab = () => React2__default.createElement(PluginSettingsPanel, {
|
|
1696
2266
|
title: "Orders Settings",
|
|
1697
2267
|
registries: ordersRegistries,
|
|
1698
2268
|
routeBase: "/settings/orders"
|
|
@@ -1718,6 +2288,6 @@ function createOrdersPlugin(options) {
|
|
|
1718
2288
|
};
|
|
1719
2289
|
}
|
|
1720
2290
|
|
|
1721
|
-
export { AIQFOME_CONNECTOR_ID, AIQFOME_SETTINGS, AUTO_ACCEPT_SETTING, IFOOD_CONNECTOR_ID, IFOOD_SETTINGS, KEETA_CONNECTOR_ID, KEETA_SETTINGS, NINETY_NINE_FOOD_CONNECTOR_ID, NINETY_NINE_FOOD_SETTINGS, PREP_TIME_SETTING, RAPPI_CONNECTOR_ID, RAPPI_SETTINGS, UBER_EATS_CONNECTOR_ID, UBER_EATS_SETTINGS, aiqfomeConnectorDef, createFayzOrdersProvider, createOrdersPlugin, ifoodConnectorDef, keetaConnectorDef, marketplacePermissions, nineNineFoodConnectorDef, rappiConnectorDef, uberEatsConnectorDef };
|
|
2291
|
+
export { AIQFOME_CONNECTOR_ID, AIQFOME_SETTINGS, AUTO_ACCEPT_SETTING, IFOOD_CONNECTOR_ID, IFOOD_SETTINGS, KEETA_CONNECTOR_ID, KEETA_SETTINGS, NINETY_NINE_FOOD_CONNECTOR_ID, NINETY_NINE_FOOD_SETTINGS, PREP_TIME_SETTING, RAPPI_CONNECTOR_ID, RAPPI_SETTINGS, UBER_EATS_CONNECTOR_ID, UBER_EATS_SETTINGS, aiqfomeConnectorDef, createCoreOrdersProvider, createFayzOrdersProvider, createOrdersPlugin, ifoodConnectorDef, keetaConnectorDef, marketplacePermissions, nineNineFoodConnectorDef, rappiConnectorDef, uberEatsConnectorDef };
|
|
1722
2292
|
//# sourceMappingURL=index.js.map
|
|
1723
2293
|
//# sourceMappingURL=index.js.map
|