This commit is contained in:
seb
2026-09-26 23:06:05 +02:00
parent e491ecfa47
commit e7f92ebef8
41 changed files with 3632 additions and 1751 deletions

View File

@@ -0,0 +1,59 @@
import { CREATE as MSSQL_CREATE } from '../../providers/mssql/queries/warehouse.js';
import { CREATE as SQLITE_CREATE } from '../../providers/sqlite3/queries/warehouse.js';
import { isUniqueConstraintError } from '../../providers/provider.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return { id: row.id, name: row.name };
}
export default function create(provider) {
const access = resolveSelectedCompanyAccess(provider);
const sql =
provider?.dialect === 'sqlite3' ? SQLITE_CREATE : MSSQL_CREATE;
/**
* Creates a new warehouse for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} input - Warehouse input.
* @param {string} input.name - The warehouse name.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, name: string}>} The created warehouse.
* @throws {Error} If name is missing or already exists.
*
* @example
* const wh = await store.warehouse.create(googleAuthId, { name: 'Hauptlager' });
* // => { id: 1, name: 'Hauptlager' }
*/
return async (googleAuthId, { name } = {}, tx) => {
const cleaned = typeof name === 'string' ? name.trim() : '';
if (!cleaned) {
throw new Error('Name required');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
try {
const result = await runner.query(sql, {
companyId,
name: cleaned,
});
const row = result.rows[0] ?? { id: result.insertId, name: cleaned };
return shape(row);
} catch (err) {
if (
isUniqueConstraintError(err) ||
err?.number === 2627 ||
err?.number === 2601 ||
err?.code === 'SQLITE_CONSTRAINT_UNIQUE'
) {
throw new Error('Warehouse already exists');
}
throw err;
}
};
}

View File

@@ -0,0 +1,62 @@
import {
DELETE_BY_COMPANY_AND_ID as MSSQL_DELETE,
CHECK_LEDGER_USAGE as MSSQL_CHECK_USAGE,
} from '../../providers/mssql/queries/warehouse.js';
import {
DELETE_BY_COMPANY_AND_ID as SQLITE_DELETE,
CHECK_LEDGER_USAGE as SQLITE_CHECK_USAGE,
} from '../../providers/sqlite3/queries/warehouse.js';
import { isForeignKeyConstraintError } from '../../providers/provider.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
export default function del(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const deleteSql = isSqlite ? SQLITE_DELETE : MSSQL_DELETE;
const checkUsageSql = isSqlite ? SQLITE_CHECK_USAGE : MSSQL_CHECK_USAGE;
/**
* Deletes a warehouse for the user's selected company, unless referenced in the ledger.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The warehouse ID to delete.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<void>}
* @throws {Error} If warehouse not found or is in use in the ledger.
*
* @example
* await store.warehouse.delete(googleAuthId, 1);
*/
return async (googleAuthId, id, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
throw new Error('Warehouse not found');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
// Pre-check if warehouse is referenced in the ledger
const usage = await runner.query(checkUsageSql, { warehouseId: numericId });
if (usage.rows && usage.rows.length > 0) {
throw new Error('Warehouse in use in ledger');
}
try {
const result = await runner.query(deleteSql, {
id: numericId,
companyId,
});
if (!result.rowsAffected) {
throw new Error('Warehouse not found');
}
} catch (err) {
if (isForeignKeyConstraintError(err)) {
throw new Error('Warehouse in use in ledger');
}
throw err;
}
};
}

View File

@@ -0,0 +1,44 @@
import { GET_BY_COMPANY_AND_ID as MSSQL_GET_BY_ID } from '../../providers/mssql/queries/warehouse.js';
import { GET_BY_COMPANY_AND_ID as SQLITE_GET_BY_ID } from '../../providers/sqlite3/queries/warehouse.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return { id: row.id, name: row.name };
}
export default function getById(provider) {
const access = resolveSelectedCompanyAccess(provider);
const sql =
provider?.dialect === 'sqlite3' ? SQLITE_GET_BY_ID : MSSQL_GET_BY_ID;
/**
* Retrieves a warehouse by its ID for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The warehouse ID.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, name: string}|undefined>} The warehouse or undefined.
*
* @example
* const wh = await store.warehouse.getById(googleAuthId, 1);
* // => { id: 1, name: 'Hauptlager' }
*/
return async (googleAuthId, id, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
return undefined;
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, {
id: numericId,
companyId,
});
const row = result.rows[0];
return row ? shape(row) : undefined;
};
}

View File

@@ -0,0 +1,33 @@
import { LIST_BY_COMPANY as MSSQL_LIST } from '../../providers/mssql/queries/warehouse.js';
import { LIST_BY_COMPANY as SQLITE_LIST } from '../../providers/sqlite3/queries/warehouse.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return { id: row.id, name: row.name };
}
export default function list(provider) {
const access = resolveSelectedCompanyAccess(provider);
const sql =
provider?.dialect === 'sqlite3' ? SQLITE_LIST : MSSQL_LIST;
/**
* Lists all warehouses for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<Array<{id: number, name: string}>>} List of warehouses.
*
* @example
* const warehouses = await store.warehouse.list(googleAuthId);
* // => [{ id: 1, name: 'Hauptlager' }]
*/
return async (googleAuthId, tx) => {
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, { companyId });
return result.rows.map(shape);
};
}

View File

@@ -0,0 +1,77 @@
import {
GET_BY_COMPANY_AND_ID as MSSQL_GET_BY_ID,
UPDATE_BY_ID as MSSQL_UPDATE,
} from '../../providers/mssql/queries/warehouse.js';
import {
GET_BY_COMPANY_AND_ID as SQLITE_GET_BY_ID,
UPDATE_BY_ID as SQLITE_UPDATE,
} from '../../providers/sqlite3/queries/warehouse.js';
import { isUniqueConstraintError } from '../../providers/provider.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return { id: row.id, name: row.name };
}
export default function update(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const getSql = isSqlite ? SQLITE_GET_BY_ID : MSSQL_GET_BY_ID;
const updateSql = isSqlite ? SQLITE_UPDATE : MSSQL_UPDATE;
/**
* Updates a warehouse name for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The warehouse ID to update.
* @param {object} input - Update payload.
* @param {string} input.name - The new warehouse name.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, name: string}>} The updated warehouse.
* @throws {Error} If warehouse not found, name is missing, or name already exists.
*
* @example
* const updated = await store.warehouse.update(googleAuthId, 1, { name: 'Zentrallager' });
* // => { id: 1, name: 'Zentrallager' }
*/
return async (googleAuthId, id, { name } = {}, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
throw new Error('Warehouse not found');
}
const cleaned = typeof name === 'string' ? name.trim() : '';
if (!cleaned) {
throw new Error('Name required');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const existing = await runner.query(getSql, { id: numericId, companyId });
if (!existing.rows[0]) {
throw new Error('Warehouse not found');
}
try {
await runner.query(updateSql, {
id: numericId,
companyId,
name: cleaned,
});
return shape({ id: numericId, name: cleaned });
} catch (err) {
if (
isUniqueConstraintError(err) ||
err?.number === 2627 ||
err?.number === 2601 ||
err?.code === 'SQLITE_CONSTRAINT_UNIQUE'
) {
throw new Error('Warehouse already exists');
}
throw err;
}
};
}