-
Notifications
You must be signed in to change notification settings - Fork 196
feat: enhance offline batch and serial number management #359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
7889034
4b5eb31
59e6f51
65fba4f
b2adb1c
ddf82e2
bd6f34d
3d87924
93d5887
438f9dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,23 +146,57 @@ export const getCachedSerialData = async (itemCode) => { | |
| } | ||
| }; | ||
|
|
||
| export function parseSerialNumbers(serialNumbers) { | ||
| if (!serialNumbers) return []; | ||
| return Array.isArray(serialNumbers) | ||
| ? serialNumbers | ||
| : String(serialNumbers) | ||
| .split("\n") | ||
| .map((s) => s.trim()) | ||
| .filter(Boolean); | ||
| } | ||
|
|
||
| // Persist batch/serial data for a single cached item (partial updates supported) | ||
| export const persistItemBatchSerialData = async (itemCode, data) => { | ||
| try { | ||
| if (!itemCode || !data) return false; | ||
|
|
||
| const update = {}; | ||
| if (data.batch_no_data !== undefined) update.batch_no_data = data.batch_no_data; | ||
| if (data.serial_no_data !== undefined) update.serial_no_data = data.serial_no_data; | ||
|
|
||
| if (Object.keys(update).length === 0) return false; | ||
|
|
||
| return await db.transaction("rw", db.items, async () => { | ||
| const item = await db.items.get(itemCode); | ||
| if (!item) { | ||
| // Avoid phantom rows (no item_name/barcodes) that blank the product grid | ||
| return false; | ||
| } | ||
|
|
||
| await db.items.update(itemCode, update); | ||
| return true; | ||
| }); | ||
| } catch (error) { | ||
| console.error("Error persisting item batch/serial data:", error); | ||
| return false; | ||
| } | ||
| }; | ||
|
|
||
| // Update batch/serial data for items in cache | ||
| export const updateItemBatchSerialData = async (batchSerialDataMap) => { | ||
| try { | ||
| if (!batchSerialDataMap || Object.keys(batchSerialDataMap).length === 0) return; | ||
|
|
||
| // Update each item with its batch/serial data | ||
| const updates = Object.entries(batchSerialDataMap).map(async ([itemCode, data]) => { | ||
| const item = await db.items.get(itemCode); | ||
| if (item) { | ||
| await db.items.update(itemCode, { | ||
| await db.transaction("rw", db.items, async () => { | ||
| for (const [itemCode, data] of Object.entries(batchSerialDataMap)) { | ||
| await persistItemBatchSerialData(itemCode, { | ||
| batch_no_data: data.batch_no_data || [], | ||
| serial_no_data: data.serial_no_data || [], | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| await Promise.all(updates); | ||
| console.log( | ||
| `Updated batch/serial data for ${Object.keys(batchSerialDataMap).length} items` | ||
| ); | ||
|
|
@@ -173,6 +207,60 @@ export const updateItemBatchSerialData = async (batchSerialDataMap) => { | |
| } | ||
| }; | ||
|
|
||
| // Remove consumed serial numbers from offline cache | ||
| export const consumeCachedSerials = async (itemCode, serialNumbers) => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 T4 — All four cache mutators are non-atomic read-modify-write
Two concurrent consumes, or a consume racing the background await db.transaction("rw", db.items, async () => {
const serials = await getCachedSerialData(itemCode);
...
});Applies to whichever of these survives the split.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair point — fire-and-forget Done in
( |
||
| try { | ||
| if (!itemCode) return; | ||
|
|
||
| await db.transaction("rw", db.items, async () => { | ||
| const item = await db.items.get(itemCode); | ||
| const serials = item?.serial_no_data || []; | ||
| if (!serials.length) return; | ||
|
|
||
| const toRemove = new Set(parseSerialNumbers(serialNumbers)); | ||
| const remaining = serials.filter((s) => !toRemove.has(s.serial_no)); | ||
|
|
||
| await db.items.update(itemCode, { serial_no_data: remaining }); | ||
| }); | ||
| } catch (error) { | ||
| console.error("Error consuming cached serials:", error); | ||
| } | ||
| }; | ||
|
|
||
| // Return serial numbers to offline cache (e.g. item removed from cart) | ||
| export const returnCachedSerials = async (itemCode, serialNumbers, warehouse = null) => { | ||
| try { | ||
| if (!itemCode) return; | ||
|
|
||
| await db.transaction("rw", db.items, async () => { | ||
| const item = await db.items.get(itemCode); | ||
| if (!item) return; | ||
|
|
||
| const serials = item.serial_no_data || []; | ||
| const toReturn = parseSerialNumbers(serialNumbers); | ||
| if (!toReturn.length) return; | ||
|
|
||
| const scopedWarehouse = warehouse || serials[0]?.warehouse; | ||
| if (!scopedWarehouse) return; | ||
|
|
||
| const existing = new Set(serials.map((s) => s.serial_no)); | ||
| const added = toReturn | ||
| .filter((serialNo) => !existing.has(serialNo)) | ||
| .map((serial_no) => ({ serial_no, warehouse: scopedWarehouse })); | ||
|
|
||
| if (!added.length) return; | ||
|
|
||
| const merged = [...serials, ...added].sort((a, b) => | ||
| a.serial_no.localeCompare(b.serial_no, undefined, { numeric: true }) | ||
| ); | ||
|
|
||
| await db.items.update(itemCode, { serial_no_data: merged }); | ||
| }); | ||
| } catch (error) { | ||
| console.error("Error returning cached serials:", error); | ||
| } | ||
| }; | ||
|
|
||
| // Get item with price | ||
| export const getItemWithPrice = async (itemCode, priceList) => { | ||
| try { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.