u
This commit is contained in:
@@ -2,6 +2,10 @@ import numberAutomat from '../automats/numberAutomat';
|
||||
import quoteAutomat from '../automats/quoteAutomat';
|
||||
import orderAutomat from '../automats/orderAutomat';
|
||||
import deliveryAutomat from '../automats/deliveryAutomat';
|
||||
import { order as orderApi, quote as quoteApi, delivery as deliveryApi } from '../api';
|
||||
|
||||
/** Highest integer counter found per document kind. */
|
||||
const maxKnownNumberByKind = new Map();
|
||||
|
||||
/**
|
||||
* Formats a number pattern given prefix, padding width, and counter.
|
||||
@@ -20,6 +24,7 @@ export function formatNumberPattern(prefix, padding, next) {
|
||||
|
||||
/**
|
||||
* Returns the next document number for quote, order, or delivery based on company sequences.
|
||||
* Always verifies that the returned number does not collide with existing documents.
|
||||
*
|
||||
* @param {'quote'|'order'|'delivery'|string} kind
|
||||
* @returns {string}
|
||||
@@ -27,59 +32,63 @@ export function formatNumberPattern(prefix, padding, next) {
|
||||
export function getNextDocumentNumber(kind) {
|
||||
const numbers = numberAutomat.state.items ?? [];
|
||||
const seq = numbers.find((r) => r.type === kind);
|
||||
if (seq) {
|
||||
return formatNumberPattern(seq.prefix, seq.padding, seq.next);
|
||||
}
|
||||
|
||||
// Detect style from existing company sequences (sku, delivery, entity)
|
||||
const skuSeq = numbers.find((r) => r.type === 'sku');
|
||||
const deliverySeq = numbers.find((r) => r.type === 'delivery');
|
||||
const entitySeq = numbers.find((r) => r.type === 'entity');
|
||||
const refSeq = skuSeq || deliverySeq || entitySeq;
|
||||
|
||||
const pad = refSeq ? Math.max(0, Number(refSeq.padding) || 0) : 6;
|
||||
const prefixStyle = refSeq?.prefix ?? '';
|
||||
|
||||
let prefix = '';
|
||||
let pad = 6;
|
||||
let startNum = 1;
|
||||
|
||||
if (kind === 'delivery') {
|
||||
prefix = prefixStyle.startsWith('DEL-') ? 'DEL-' : (prefixStyle.startsWith('L-') ? 'L-' : 'LS-');
|
||||
} else if (
|
||||
prefixStyle.startsWith('ART-') ||
|
||||
prefixStyle.startsWith('LS-') ||
|
||||
prefixStyle.startsWith('KD-') ||
|
||||
prefixStyle.endsWith('-')
|
||||
) {
|
||||
// Standard German ERP prefix style (ART-, KD-, LS-, KORR-)
|
||||
prefix = kind === 'quote' ? 'ANG-' : (kind === 'order' ? 'AUF-' : '');
|
||||
} else if (
|
||||
prefixStyle.startsWith('SKU-') ||
|
||||
prefixStyle.startsWith('DEL-') ||
|
||||
prefixStyle.startsWith('C-')
|
||||
) {
|
||||
prefix = kind === 'quote' ? 'Q-' : (kind === 'order' ? 'ORD-' : '');
|
||||
} else if (
|
||||
prefixStyle.startsWith('A-') ||
|
||||
prefixStyle.startsWith('L-') ||
|
||||
prefixStyle.startsWith('K-')
|
||||
) {
|
||||
prefix = kind === 'quote' ? 'AG-' : (kind === 'order' ? 'AU-' : '');
|
||||
} else if (!prefixStyle && refSeq?.next >= 10000) {
|
||||
prefix = '';
|
||||
startNum = kind === 'quote' ? 40001 : 50001;
|
||||
if (seq) {
|
||||
prefix = seq.prefix ?? '';
|
||||
pad = Math.max(0, Number(seq.padding) || 0);
|
||||
startNum = Math.max(1, Number(seq.next) || 1);
|
||||
} else {
|
||||
prefix = kind === 'quote' ? 'ANG-' : (kind === 'order' ? 'AUF-' : '');
|
||||
// Detect style from existing company sequences (sku, delivery, entity)
|
||||
const skuSeq = numbers.find((r) => r.type === 'sku');
|
||||
const deliverySeq = numbers.find((r) => r.type === 'delivery');
|
||||
const entitySeq = numbers.find((r) => r.type === 'entity');
|
||||
const refSeq = skuSeq || deliverySeq || entitySeq;
|
||||
|
||||
pad = refSeq ? Math.max(0, Number(refSeq.padding) || 0) : 6;
|
||||
const prefixStyle = refSeq?.prefix ?? '';
|
||||
|
||||
if (kind === 'delivery') {
|
||||
prefix = prefixStyle.startsWith('DEL-') ? 'DEL-' : (prefixStyle.startsWith('L-') ? 'L-' : 'LS-');
|
||||
} else if (
|
||||
prefixStyle.startsWith('ART-') ||
|
||||
prefixStyle.startsWith('LS-') ||
|
||||
prefixStyle.startsWith('KD-') ||
|
||||
prefixStyle.endsWith('-')
|
||||
) {
|
||||
// Standard German ERP prefix style (ART-, KD-, LS-, KORR-)
|
||||
prefix = kind === 'quote' ? 'ANG-' : (kind === 'order' ? 'AUF-' : '');
|
||||
} else if (
|
||||
prefixStyle.startsWith('SKU-') ||
|
||||
prefixStyle.startsWith('DEL-') ||
|
||||
prefixStyle.startsWith('C-')
|
||||
) {
|
||||
prefix = kind === 'quote' ? 'Q-' : (kind === 'order' ? 'ORD-' : '');
|
||||
} else if (
|
||||
prefixStyle.startsWith('A-') ||
|
||||
prefixStyle.startsWith('L-') ||
|
||||
prefixStyle.startsWith('K-')
|
||||
) {
|
||||
prefix = kind === 'quote' ? 'AG-' : (kind === 'order' ? 'AU-' : '');
|
||||
} else if (!prefixStyle && refSeq?.next >= 10000) {
|
||||
prefix = '';
|
||||
startNum = kind === 'quote' ? 40001 : 50001;
|
||||
} else {
|
||||
prefix = kind === 'quote' ? 'ANG-' : (kind === 'order' ? 'AUF-' : '');
|
||||
}
|
||||
}
|
||||
|
||||
// Check existing docs from quoteAutomat / orderAutomat / deliveryAutomat to avoid collisions
|
||||
const listAutomat = kind === 'quote' ? quoteAutomat : (kind === 'order' ? orderAutomat : (kind === 'delivery' ? deliveryAutomat : null));
|
||||
const existingDocs = Array.isArray(listAutomat?.state?.items) ? listAutomat.state.items : [];
|
||||
let maxFound = startNum - 1;
|
||||
let maxFound = Math.max(startNum - 1, maxKnownNumberByKind.get(kind) ?? 0);
|
||||
|
||||
for (const doc of existingDocs) {
|
||||
if (!doc || typeof doc !== 'object') continue;
|
||||
const docNum = String(doc.number ?? '').trim();
|
||||
const docNum = String(doc.number ?? doc.orderNumber ?? '').trim();
|
||||
if (!docNum) continue;
|
||||
if (prefix && docNum.startsWith(prefix)) {
|
||||
const rest = docNum.slice(prefix.length);
|
||||
@@ -95,12 +104,15 @@ export function getNextDocumentNumber(kind) {
|
||||
}
|
||||
}
|
||||
|
||||
maxKnownNumberByKind.set(kind, maxFound);
|
||||
|
||||
const nextVal = maxFound >= startNum ? maxFound + 1 : startNum;
|
||||
return formatNumberPattern(prefix, pad, nextVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously ensures required stores are loaded, then returns the next document number.
|
||||
* Asynchronously ensures required stores are loaded, queries recent documents to detect
|
||||
* the highest existing number, then returns the next document number.
|
||||
*
|
||||
* @param {'quote'|'order'|'delivery'|string} kind
|
||||
* @returns {Promise<string>}
|
||||
@@ -111,6 +123,51 @@ export async function ensureDocumentNumber(kind) {
|
||||
numberAutomat.actions.ensureLoaded().catch(() => {}),
|
||||
listAutomat?.actions?.ensureLoaded ? listAutomat.actions.ensureLoaded().catch(() => {}) : Promise.resolve(),
|
||||
]);
|
||||
|
||||
try {
|
||||
let recentDocs = [];
|
||||
if (kind === 'order') {
|
||||
const res = await orderApi.list({ offset: 0, limit: 10 });
|
||||
recentDocs = Array.isArray(res?.orders) ? res.orders : [];
|
||||
} else if (kind === 'quote') {
|
||||
const res = await quoteApi.list({ offset: 0, limit: 10 });
|
||||
recentDocs = Array.isArray(res?.quotes) ? res.quotes : [];
|
||||
} else if (kind === 'delivery') {
|
||||
const res = await deliveryApi.list({ offset: 0, limit: 10 });
|
||||
recentDocs = Array.isArray(res?.deliveries) ? res.deliveries : [];
|
||||
}
|
||||
|
||||
if (recentDocs.length > 0) {
|
||||
const numbers = numberAutomat.state.items ?? [];
|
||||
const seq = numbers.find((r) => r.type === kind);
|
||||
let prefix = seq?.prefix;
|
||||
if (prefix == null) {
|
||||
prefix = kind === 'quote' ? 'ANG-' : (kind === 'order' ? 'AUF-' : 'LS-');
|
||||
}
|
||||
|
||||
let maxFound = maxKnownNumberByKind.get(kind) ?? 0;
|
||||
for (const doc of recentDocs) {
|
||||
const docNum = String(doc.number ?? doc.orderNumber ?? '').trim();
|
||||
if (!docNum) continue;
|
||||
if (prefix && docNum.startsWith(prefix)) {
|
||||
const rest = docNum.slice(prefix.length);
|
||||
const parsed = parseInt(rest, 10);
|
||||
if (!Number.isNaN(parsed) && parsed > maxFound) {
|
||||
maxFound = parsed;
|
||||
}
|
||||
} else if (!prefix) {
|
||||
const parsed = parseInt(docNum, 10);
|
||||
if (!Number.isNaN(parsed) && parsed > maxFound) {
|
||||
maxFound = parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
maxKnownNumberByKind.set(kind, maxFound);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[documentNumber] ensureDocumentNumber error fetching latest ${kind}:`, err);
|
||||
}
|
||||
|
||||
return getNextDocumentNumber(kind);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user