This commit is contained in:
seb
2026-10-14 00:10:50 +02:00
parent f6cb3d3e35
commit 56a86fc005
6 changed files with 148 additions and 12 deletions

112
src/utils/documentNumber.js Normal file
View File

@@ -0,0 +1,112 @@
import numberAutomat from '../automats/numberAutomat';
import quoteAutomat from '../automats/quoteAutomat';
import orderAutomat from '../automats/orderAutomat';
/**
* Formats a number pattern given prefix, padding width, and counter.
*
* @param {string} [prefix='']
* @param {number} [padding=0]
* @param {number} [next=1]
* @returns {string}
*/
export function formatNumberPattern(prefix, padding, next) {
const pad = Math.max(0, Number(padding) || 0);
const num = Math.max(1, Number(next) || 1);
const numStr = pad > 0 ? String(num).padStart(pad, '0') : String(num);
return `${prefix ?? ''}${numStr}`;
}
/**
* Returns the next document number for quote or order based on company sequences.
*
* @param {'quote'|'order'|string} kind
* @returns {string}
*/
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 startNum = 1;
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 to avoid collisions
const listAutomat = kind === 'quote' ? quoteAutomat : (kind === 'order' ? orderAutomat : null);
const existingDocs = Array.isArray(listAutomat?.state?.items) ? listAutomat.state.items : [];
let maxFound = startNum - 1;
for (const doc of existingDocs) {
if (!doc || typeof doc !== 'object') continue;
const docNum = String(doc.number ?? '').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 >= startNum && parsed > maxFound) {
maxFound = parsed;
}
}
}
const nextVal = maxFound >= startNum ? maxFound + 1 : startNum;
return formatNumberPattern(prefix, pad, nextVal);
}
/**
* Asynchronously ensures required stores are loaded, then returns the next document number.
*
* @param {'quote'|'order'|string} kind
* @returns {Promise<string>}
*/
export async function ensureDocumentNumber(kind) {
const listAutomat = kind === 'quote' ? quoteAutomat : (kind === 'order' ? orderAutomat : null);
await Promise.all([
numberAutomat.actions.ensureLoaded().catch(() => {}),
listAutomat?.actions?.ensureLoaded ? listAutomat.actions.ensureLoaded().catch(() => {}) : Promise.resolve(),
]);
return getNextDocumentNumber(kind);
}