@apptimate/ui 4.8.0 → 4.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apptimate/ui",
3
- "version": "4.8.0",
3
+ "version": "4.9.0",
4
4
  "main": "src/index.tsx",
5
5
  "types": "src/index.tsx",
6
6
  "dependencies": {
@@ -14,7 +14,7 @@ interface SerialSelectionModalProps {
14
14
  allowCreate?: boolean;
15
15
  initialSerials?: string[];
16
16
  warehouseId?: number;
17
- onConfirm: (serials: string[]) => void;
17
+ onConfirm: (serials: string[], serialObjects?: {id: number, serial_number: string}[]) => void;
18
18
  }
19
19
 
20
20
  export function SerialSelectionModal({
@@ -119,7 +119,15 @@ export function SerialSelectionModal({
119
119
  toast.error("Please add at least one serial number.");
120
120
  return;
121
121
  }
122
- onConfirm(serials);
122
+
123
+ if (!allowCreate) {
124
+ const serialObjects = availableSerials
125
+ .filter(s => serials.includes(s.serial_number))
126
+ .map(s => ({ id: s.id, serial_number: s.serial_number }));
127
+ onConfirm(serials, serialObjects);
128
+ } else {
129
+ onConfirm(serials);
130
+ }
123
131
  };
124
132
 
125
133
  // Display available serials from backend
@@ -20,7 +20,7 @@ interface ProductSelectionPanelProps {
20
20
  warehouseId?: number;
21
21
  partyId?: number | null;
22
22
  lineItems: TransactionLineItem[];
23
- onAddItem: (item: TransactionItem, variant?: any, extraData?: { quantity?: number; batches?: any[]; serial_numbers?: string[] }) => void;
23
+ onAddItem: (item: TransactionItem, variant?: any, extraData?: { quantity?: number; batches?: any[]; serial_numbers?: string[]; serial_number_objects?: {id: number, serial_number: string}[] }) => void;
24
24
  onUpdateLine: (uid: string, updates: Partial<TransactionLineItem>) => void;
25
25
  onRemoveLine: (uid: string) => void;
26
26
  onToggleExpand: (uid: string) => void;
@@ -96,7 +96,7 @@ export function ProductSelectionPanel({
96
96
  onAddItem(entity.item, entity.variant, { serial_numbers: [entity.serial_number], quantity: 1 });
97
97
  } else if (matchType === 'batch') {
98
98
  const batchVariant = entity.variant || (entity.variant_id && entity.item?.variants ? entity.item.variants.find((v: any) => v.id === entity.variant_id) : null);
99
- if (['sales', 'quotation'].includes(config.type)) {
99
+ if (['sales', 'quotation', 'pos'].includes(config.type)) {
100
100
  onAddItem(entity.item, batchVariant, {
101
101
  batches: [{ batch_id: entity.id, batch_number: entity.batch_number, selling_price: entity.selling_price, quantity: 1 }],
102
102
  quantity: 1
@@ -132,7 +132,7 @@ export function ProductSelectionPanel({
132
132
  setShowResults(false);
133
133
 
134
134
  if (!config.disableTrackingSelection && item.tracking_type === 'batch') {
135
- if (['sales', 'quotation'].includes(config.type)) {
135
+ if (['sales', 'quotation', 'pos'].includes(config.type)) {
136
136
  onAddItem(item, variant);
137
137
  } else {
138
138
  setActiveBatchItem({ item, variant });
@@ -167,10 +167,10 @@ export function ProductSelectionPanel({
167
167
  }
168
168
  };
169
169
 
170
- const handleSerialConfirm = (serials: string[]) => {
170
+ const handleSerialConfirm = (serials: string[], serialObjects?: {id: number, serial_number: string}[]) => {
171
171
  if (!activeSerialItem) return;
172
172
 
173
- onAddItem(activeSerialItem.item, activeSerialItem.variant, { serial_numbers: serials, quantity: serials.length });
173
+ onAddItem(activeSerialItem.item, activeSerialItem.variant, { serial_numbers: serials, serial_number_objects: serialObjects, quantity: serials.length });
174
174
  setActiveSerialItem(null);
175
175
  };
176
176
 
@@ -582,7 +582,7 @@ export function ProductSelectionPanel({
582
582
  </THeader>
583
583
  <TBody>
584
584
  {lineItems.map((line) => {
585
- const isBatchPending = ['sales', 'quotation'].includes(config.type) && line.item.tracking_type === 'batch' && (!line.batches || line.batches.length === 0);
585
+ const isBatchPending = ['sales', 'quotation', 'pos'].includes(config.type) && line.item.tracking_type === 'batch' && (!line.batches || line.batches.length === 0);
586
586
  return (
587
587
  <TRow key={line.uid}>
588
588
  <TCell label="Item" className="py-2.5">
@@ -604,14 +604,20 @@ export function ProductSelectionPanel({
604
604
  )}
605
605
  </span>
606
606
  <span className="text-[11px] text-foreground-subtle font-mono mt-0.5 break-words">
607
- {line.item.has_variants && line.variant_id && line.item.variants?.find(v => v.id === line.variant_id)
608
- ? `${line.item.variants.find(v => v.id === line.variant_id)?.variant_name} • ${line.item.variants.find(v => v.id === line.variant_id)?.sku}`
609
- : line.item.sku}
607
+ {(() => {
608
+ if (line.item.has_variants && line.variant_id) {
609
+ const variant = line.variant || line.item.variants?.find(v => v.id === line.variant_id);
610
+ if (variant) {
611
+ return `${variant.variant_name} • ${variant.sku}`;
612
+ }
613
+ }
614
+ return line.item.sku || '';
615
+ })()}
610
616
  </span>
611
- {((!config.disableTrackingSelection && (line.item.tracking_type === 'batch' || line.item.tracking_type === 'serial')) || (line.item.tracking_type === 'batch' && ['sales', 'quotation'].includes(config.type))) && (
617
+ {((!config.disableTrackingSelection && (line.item.tracking_type === 'batch' || line.item.tracking_type === 'serial')) || (line.item.tracking_type === 'batch' && ['sales', 'quotation', 'pos'].includes(config.type))) && (
612
618
  <div className="flex flex-wrap gap-1 mt-1.5">
613
619
  {line.item.tracking_type === 'batch' && (() => {
614
- if (['sales', 'quotation'].includes(config.type)) {
620
+ if (['sales', 'quotation', 'pos'].includes(config.type)) {
615
621
  return (
616
622
  <div className="mt-1.5 flex items-center gap-2" onClick={(e) => e.stopPropagation()}>
617
623
  {(!line.batches || line.batches.length === 0) && (
@@ -746,11 +752,16 @@ export function ProductSelectionPanel({
746
752
  ? (() => {
747
753
  if (line.parent_line_uids.length === 1) {
748
754
  const p = lineItems.find((l) => l.uid === line.parent_line_uids?.[0]);
749
- return p
750
- ? p.item.has_variants && p.variant_id && p.item.variants?.find((v) => v.id === p.variant_id)
751
- ? `${p.item.name} (${p.item.variants.find((v) => v.id === p.variant_id)?.variant_name})`
752
- : p.item.name
753
- : "Select Parent Line...";
755
+ if (p) {
756
+ if (p.item.has_variants && p.variant_id) {
757
+ const pVariant = p.variant || p.item.variants?.find((v) => v.id === p.variant_id);
758
+ if (pVariant && pVariant.variant_name) {
759
+ return `${p.item.name} (${pVariant.variant_name})`;
760
+ }
761
+ }
762
+ return p.item.name;
763
+ }
764
+ return "Select Parent Line...";
754
765
  } else {
755
766
  return `${line.parent_line_uids.length} Parents Selected`;
756
767
  }
@@ -799,7 +810,7 @@ export function ProductSelectionPanel({
799
810
  const variantPriceField = config.priceField === 'default_cost_price' ? 'cost_price' : 'sale_price';
800
811
  const actualVariant = line.variant_id ? line.item.variants?.find((v: any) => v.id === line.variant_id) : (line.item.variants?.[0] || null);
801
812
  const batchPriceField = config.priceField === 'default_cost_price' ? 'cost_price' : 'selling_price';
802
- const batchPrice = ['sales', 'quotation'].includes(config.type) && line.batches?.[0] ? (line.batches[0] as any)[batchPriceField] : undefined;
813
+ const batchPrice = ['sales', 'quotation', 'pos'].includes(config.type) && line.batches?.[0] ? (line.batches[0] as any)[batchPriceField] : undefined;
803
814
  const basePrice = (batchPrice !== undefined && batchPrice !== null)
804
815
  ? Number(batchPrice)
805
816
  : (actualVariant
@@ -920,7 +931,7 @@ export function ProductSelectionPanel({
920
931
  warehouseId={warehouseId}
921
932
  allowCreate={config.allowCreateBatchAndSerial}
922
933
  initialSerials={lineItems.find(l => l.uid === activeSerialLineUid)?.serial_numbers || []}
923
- onConfirm={(serials) => {
934
+ onConfirm={(serials, serialObjects) => {
924
935
  const line = lineItems.find(l => l.uid === activeSerialLineUid);
925
936
  if (!line) return;
926
937
 
@@ -929,6 +940,7 @@ export function ProductSelectionPanel({
929
940
 
930
941
  onUpdateLine(line.uid, {
931
942
  serial_numbers: serials,
943
+ serial_number_objects: serialObjects,
932
944
  quantity: newQty,
933
945
  discount_amount: discAmt,
934
946
  line_total: line.unit_price * newQty - discAmt,
@@ -127,6 +127,7 @@ export function ProductTransactionScreen({
127
127
  uid: l.uid || Math.random().toString(36).slice(2, 7),
128
128
  item: l.item,
129
129
  variant_id: l.variant_id || null,
130
+ variant: l.variant || null,
130
131
  batches: l.batches || [],
131
132
  serial_numbers: l.serial_numbers || [],
132
133
  quantity: qty,
@@ -412,7 +413,9 @@ export function ProductTransactionScreen({
412
413
  expiry_date: undefined,
413
414
  supplier_reference: undefined,
414
415
  selling_price: undefined,
415
- serial_numbers: l.serial_numbers,
416
+ serial_numbers: l.serial_number_objects && l.serial_number_objects.length > 0
417
+ ? l.serial_number_objects.map(o => o.id)
418
+ : l.serial_numbers,
416
419
  quantity: l.quantity,
417
420
  uid: l.uid,
418
421
  is_free_qty: l.is_free_qty,
@@ -72,8 +72,10 @@ export interface TransactionLineItem {
72
72
  uid: string; // Client-side unique key for React
73
73
  item: TransactionItem;
74
74
  variant_id?: number | null;
75
+ variant?: TransactionItemVariant | null;
75
76
  batches?: TransactionLineBatch[];
76
77
  serial_numbers?: string[];
78
+ serial_number_objects?: {id: number, serial_number: string}[];
77
79
  quantity: number;
78
80
  is_free_qty?: boolean;
79
81
  parent_line_uids?: string[];
@@ -333,7 +335,7 @@ export interface TransactionPayload {
333
335
  expiry_date?: string;
334
336
  supplier_reference?: string;
335
337
  selling_price?: number;
336
- serial_numbers?: string[];
338
+ serial_numbers?: string[] | number[];
337
339
  quantity: number;
338
340
  uid: string;
339
341
  is_free_qty?: boolean;