u
This commit is contained in:
123
src/store/numbers/ensureType.js
Normal file
123
src/store/numbers/ensureType.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
ENSURE_TYPE as MSSQL_ENSURE_TYPE,
|
||||
GET_FOR_UPDATE as MSSQL_GET_FOR_UPDATE,
|
||||
UPDATE_BY_COMPANY_AND_ID as MSSQL_UPDATE_BY_COMPANY_AND_ID,
|
||||
} from '../../providers/mssql/queries/numbers.js';
|
||||
import {
|
||||
ENSURE_TYPE as SQLITE_ENSURE_TYPE,
|
||||
GET_FOR_UPDATE as SQLITE_GET_FOR_UPDATE,
|
||||
UPDATE_BY_COMPANY_AND_ID as SQLITE_UPDATE_BY_COMPANY_AND_ID,
|
||||
} from '../../providers/sqlite3/queries/numbers.js';
|
||||
|
||||
/**
|
||||
* Ensures a number sequence row exists for a company and type,
|
||||
* inferring the company's prefix style, padding, and next counter if not yet created or unconfigured.
|
||||
*
|
||||
* @param {object} runner - The database runner.
|
||||
* @param {number} companyId - The id of the company.
|
||||
* @param {string} type - The number type (e.g. 'order', 'quote', 'sku').
|
||||
* @returns {Promise<{id: number, type: string, prefix: string, padding: number, next: number}>} The sequence row.
|
||||
*/
|
||||
export async function ensureNumberType(runner, companyId, type) {
|
||||
const isSqlite = runner.dialect === 'sqlite3';
|
||||
const getSql = isSqlite ? SQLITE_GET_FOR_UPDATE : MSSQL_GET_FOR_UPDATE;
|
||||
const ensureSql = isSqlite ? SQLITE_ENSURE_TYPE : MSSQL_ENSURE_TYPE;
|
||||
const updateSql = isSqlite ? SQLITE_UPDATE_BY_COMPANY_AND_ID : MSSQL_UPDATE_BY_COMPANY_AND_ID;
|
||||
|
||||
let res = await runner.query(getSql, { companyId, type });
|
||||
let row = res.rows[0];
|
||||
|
||||
// If already exists and is configured (has prefix or padding > 0, or not order/quote), return row
|
||||
if (row && (row.prefix || row.padding > 0 || (type !== 'order' && type !== 'quote'))) {
|
||||
return row;
|
||||
}
|
||||
|
||||
// Find reference sequence for company to match prefix and padding style
|
||||
const listSql = isSqlite
|
||||
? 'SELECT "type", prefix, padding, "next" FROM numbers WHERE company_id = @companyId'
|
||||
: 'SELECT [type], prefix, padding, [next] FROM numbers WHERE company_id = @companyId';
|
||||
const existingSeqs = (await runner.query(listSql, { companyId })).rows;
|
||||
|
||||
const skuSeq = existingSeqs.find((r) => r.type === 'sku');
|
||||
const deliverySeq = existingSeqs.find((r) => r.type === 'delivery');
|
||||
const entitySeq = existingSeqs.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 = '';
|
||||
if (type === 'shipment') {
|
||||
prefix = '-L-';
|
||||
} else if (type === 'quote') {
|
||||
if (prefixStyle.startsWith('SKU-') || prefixStyle.startsWith('DEL-') || prefixStyle.startsWith('C-')) {
|
||||
prefix = 'Q-';
|
||||
} else if (prefixStyle.startsWith('A-') || prefixStyle.startsWith('L-') || prefixStyle.startsWith('K-')) {
|
||||
prefix = 'AG-';
|
||||
} else {
|
||||
prefix = 'ANG-';
|
||||
}
|
||||
} else if (type === 'order') {
|
||||
if (prefixStyle.startsWith('SKU-') || prefixStyle.startsWith('DEL-') || prefixStyle.startsWith('C-')) {
|
||||
prefix = 'ORD-';
|
||||
} else if (prefixStyle.startsWith('A-') || prefixStyle.startsWith('L-') || prefixStyle.startsWith('K-')) {
|
||||
prefix = 'AU-';
|
||||
} else {
|
||||
prefix = 'AUF-';
|
||||
}
|
||||
} else if (type === 'adjustment') {
|
||||
prefix = prefixStyle.endsWith('-') ? 'KORR-' : '';
|
||||
}
|
||||
|
||||
const padding = (type === 'order' || type === 'quote') ? (pad || 6) : (type === 'adjustment' ? 5 : (refSeq?.padding ?? 0));
|
||||
|
||||
// Determine next counter by scanning manifest for existing documents
|
||||
let next = 1;
|
||||
if (type === 'order' || type === 'quote') {
|
||||
const manifestSql = isSqlite
|
||||
? 'SELECT "number" FROM manifest WHERE company_id = @companyId AND "type" = @type'
|
||||
: 'SELECT [number] FROM manifest WHERE company_id = @companyId AND [type] = @type';
|
||||
const manifests = (await runner.query(manifestSql, { companyId, type })).rows;
|
||||
let maxFound = 0;
|
||||
for (const m of manifests) {
|
||||
const numStr = String(m.number ?? '').trim();
|
||||
if (prefix && numStr.startsWith(prefix)) {
|
||||
const val = parseInt(numStr.slice(prefix.length), 10);
|
||||
if (!Number.isNaN(val) && val > maxFound) maxFound = val;
|
||||
} else if (!prefix) {
|
||||
const val = parseInt(numStr, 10);
|
||||
if (!Number.isNaN(val) && val > maxFound) maxFound = val;
|
||||
}
|
||||
}
|
||||
if (maxFound > 0) {
|
||||
next = maxFound + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (row) {
|
||||
// Row existed but was empty unconfigured row (e.g. prefix: '', padding: 0)
|
||||
const effectiveNext = Math.max(next, row.next ?? 1);
|
||||
await runner.query(updateSql, {
|
||||
id: row.id,
|
||||
companyId,
|
||||
prefix,
|
||||
padding,
|
||||
next: effectiveNext,
|
||||
});
|
||||
row.prefix = prefix;
|
||||
row.padding = padding;
|
||||
row.next = effectiveNext;
|
||||
} else {
|
||||
await runner.query(ensureSql, {
|
||||
companyId,
|
||||
type,
|
||||
prefix,
|
||||
padding,
|
||||
next,
|
||||
});
|
||||
res = await runner.query(getSql, { companyId, type });
|
||||
row = res.rows[0];
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
import {
|
||||
ENSURE_TYPE as MSSQL_ENSURE_TYPE,
|
||||
LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY,
|
||||
} from '../../providers/mssql/queries/numbers.js';
|
||||
import {
|
||||
ENSURE_TYPE as SQLITE_ENSURE_TYPE,
|
||||
LIST_BY_COMPANY as SQLITE_LIST_BY_COMPANY,
|
||||
} from '../../providers/sqlite3/queries/numbers.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { ensureNumberType } from './ensureType.js';
|
||||
import makeRunner from '../runner.js';
|
||||
|
||||
const DEFAULT_TYPES = [
|
||||
{ type: 'sku', prefix: '' },
|
||||
{ type: 'entity', prefix: '' },
|
||||
{ type: 'delivery', prefix: '' },
|
||||
{ type: 'adjustment', prefix: '' },
|
||||
{ type: 'shipment', prefix: '-L-' },
|
||||
'sku',
|
||||
'entity',
|
||||
'delivery',
|
||||
'adjustment',
|
||||
'shipment',
|
||||
'quote',
|
||||
'order',
|
||||
];
|
||||
|
||||
function shape(row) {
|
||||
@@ -30,13 +31,12 @@ function shape(row) {
|
||||
export default function list(provider) {
|
||||
const access = resolveSelectedCompanyAccess(provider);
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const ensureSql = isSqlite ? SQLITE_ENSURE_TYPE : MSSQL_ENSURE_TYPE;
|
||||
const sql =
|
||||
isSqlite ? SQLITE_LIST_BY_COMPANY : MSSQL_LIST_BY_COMPANY;
|
||||
|
||||
/**
|
||||
* Lists the number sequences of the user's selected company.
|
||||
* Ensures that default sequences ('sku', 'entity', 'delivery', 'adjustment', 'shipment') exist.
|
||||
* Ensures that default sequences ('sku', 'entity', 'delivery', 'adjustment', 'shipment', 'quote', 'order') exist.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
@@ -52,8 +52,7 @@ export default function list(provider) {
|
||||
|
||||
for (const item of DEFAULT_TYPES) {
|
||||
const type = typeof item === 'string' ? item : item.type;
|
||||
const prefix = typeof item === 'string' ? (type === 'shipment' ? '-L-' : '') : item.prefix;
|
||||
await runner.query(ensureSql, { companyId, type, prefix });
|
||||
await ensureNumberType(runner, companyId, type);
|
||||
}
|
||||
|
||||
const result = await runner.query(sql, { companyId });
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {
|
||||
ENSURE_TYPE as MSSQL_ENSURE_TYPE,
|
||||
GET_FOR_UPDATE as MSSQL_GET_FOR_UPDATE,
|
||||
INCREMENT as MSSQL_INCREMENT,
|
||||
} from '../../providers/mssql/queries/numbers.js';
|
||||
import {
|
||||
ENSURE_TYPE as SQLITE_ENSURE_TYPE,
|
||||
GET_FOR_UPDATE as SQLITE_GET_FOR_UPDATE,
|
||||
INCREMENT as SQLITE_INCREMENT,
|
||||
} from '../../providers/sqlite3/queries/numbers.js';
|
||||
import { ensureNumberType } from './ensureType.js';
|
||||
import makeRunner from '../runner.js';
|
||||
|
||||
function formatNumber(prefix, padding, num) {
|
||||
@@ -19,8 +16,6 @@ function formatNumber(prefix, padding, num) {
|
||||
|
||||
export default function nextNumber(provider) {
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const ensureSql = isSqlite ? SQLITE_ENSURE_TYPE : MSSQL_ENSURE_TYPE;
|
||||
const getSql = isSqlite ? SQLITE_GET_FOR_UPDATE : MSSQL_GET_FOR_UPDATE;
|
||||
const incSql = isSqlite ? SQLITE_INCREMENT : MSSQL_INCREMENT;
|
||||
|
||||
/**
|
||||
@@ -37,10 +32,7 @@ export default function nextNumber(provider) {
|
||||
*/
|
||||
return async (companyId, type, tx) => {
|
||||
const doAlloc = async (runner) => {
|
||||
const prefix = type === 'shipment' ? '-L-' : '';
|
||||
await runner.query(ensureSql, { companyId, type, prefix });
|
||||
const res = await runner.query(getSql, { companyId, type });
|
||||
const row = res.rows[0];
|
||||
const row = await ensureNumberType(runner, companyId, type);
|
||||
if (!row) {
|
||||
throw new Error('Number type not found for company');
|
||||
}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
ENSURE_TYPE as MSSQL_ENSURE_TYPE,
|
||||
GET_FOR_UPDATE as MSSQL_GET_FOR_UPDATE,
|
||||
} from '../../providers/mssql/queries/numbers.js';
|
||||
import {
|
||||
ENSURE_TYPE as SQLITE_ENSURE_TYPE,
|
||||
GET_FOR_UPDATE as SQLITE_GET_FOR_UPDATE,
|
||||
} from '../../providers/sqlite3/queries/numbers.js';
|
||||
import { ensureNumberType } from './ensureType.js';
|
||||
import makeRunner from '../runner.js';
|
||||
|
||||
function formatNumber(prefix, padding, num) {
|
||||
@@ -16,10 +9,6 @@ function formatNumber(prefix, padding, num) {
|
||||
}
|
||||
|
||||
export default function peekNumber(provider) {
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const ensureSql = isSqlite ? SQLITE_ENSURE_TYPE : MSSQL_ENSURE_TYPE;
|
||||
const getSql = isSqlite ? SQLITE_GET_FOR_UPDATE : MSSQL_GET_FOR_UPDATE;
|
||||
|
||||
/**
|
||||
* Returns the next number for a company and type without consuming it.
|
||||
*
|
||||
@@ -34,10 +23,7 @@ export default function peekNumber(provider) {
|
||||
*/
|
||||
return async (companyId, type, tx) => {
|
||||
const doPeek = async (runner) => {
|
||||
const prefix = type === 'shipment' ? '-L-' : '';
|
||||
await runner.query(ensureSql, { companyId, type, prefix });
|
||||
const res = await runner.query(getSql, { companyId, type });
|
||||
const row = res.rows[0];
|
||||
const row = await ensureNumberType(runner, companyId, type);
|
||||
if (!row) {
|
||||
throw new Error('Number type not found for company');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user