Add entity customer numbers and scope APIs via selected company.
Entity gets an optional unique-per-company number with allocate/peek/isNumberFree helpers. Company-scoped store methods no longer take companyId and resolve google_auth.selected_company instead.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { GET_ARTICLE_EXPORT, GET_CATEGORY_TREE_FLAT } from '../../sql/article.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveCategoryAccess from '../category/resolveCategoryAccess.js';
|
||||
|
||||
const ROOT_CATEGORY_NAME = 'Home';
|
||||
@@ -19,18 +19,16 @@ function parseJsonArray(raw) {
|
||||
*
|
||||
* @example
|
||||
* const fn = listExport(pool);
|
||||
* const articles = await fn(42, 1, null);
|
||||
* const articles = await fn(42, null);
|
||||
* // => [{ id, sku, name, slug, unitSi, net, taxRate, eans, categoryBreadcrumbs, fullHashes, ... }]
|
||||
*/
|
||||
export default function listExport(pool) {
|
||||
const companyAccess = resolveCompanyAccess(pool);
|
||||
const companyAccess = resolveSelectedCompanyAccess(pool);
|
||||
const categoryAccess = resolveCategoryAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, categoryId) => {
|
||||
const { languageId, posCustomerGroupId, rootCategoryId } = await companyAccess(
|
||||
googleAuthId,
|
||||
companyId,
|
||||
);
|
||||
return async (googleAuthId, categoryId) => {
|
||||
const { languageId, posCustomerGroupId, rootCategoryId } =
|
||||
await companyAccess(googleAuthId);
|
||||
|
||||
// Default to the company's root category when no specific category is given.
|
||||
let exportCategoryId = rootCategoryId;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SEARCH_ARTICLES } from '../../sql/article.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Returns a lightweight search index of all articles for a company.
|
||||
@@ -9,14 +9,14 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = listSearchIndex(pool);
|
||||
* const articles = await fn(42, 1);
|
||||
* const articles = await fn(42);
|
||||
* // => [{ id, sku, name, eans, categoryIds }]
|
||||
*/
|
||||
export default function listSearchIndex(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
const { rootCategoryId, languageId } = await access(googleAuthId, companyId);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId, rootCategoryId, languageId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_COMPANY_AND_ID } from '../../sql/brand.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a brand for a company by its id.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 3);
|
||||
* await fn(42, 3);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Brand not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
try {
|
||||
const result = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LIST_BY_COMPANY } from '../../sql/brand.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Lists the brands of a company.
|
||||
@@ -9,14 +9,14 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const brands = await fn(42, 1);
|
||||
* const brands = await fn(42);
|
||||
* // => [{ id, name, gsprpinfo }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
GET_BY_COMPANY_AND_NAME,
|
||||
UPDATE_BY_ID,
|
||||
} from '../../sql/brand.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Creates or updates a brand for a company, keyed by name.
|
||||
@@ -13,20 +13,20 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = upsert(pool);
|
||||
* const brand = await fn(42, 1, { name: 'Coca-Cola', gsprpinfo: 'x' });
|
||||
* const brand = await fn(42, { name: 'Coca-Cola', gsprpinfo: 'x' });
|
||||
* // => { id, name, gsprpinfo }
|
||||
*/
|
||||
export default function upsert(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, input = {}) => {
|
||||
return async (googleAuthId, input = {}) => {
|
||||
const name = input.name?.trim();
|
||||
if (!name) {
|
||||
throw new Error('Invalid brand');
|
||||
}
|
||||
const gsprpinfo = input.gsprpinfo ?? null;
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const existing = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_TREE_FOR_COMPANY } from '../../sql/category.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import queryCategoryArticles from './queryCategoryArticles.js';
|
||||
import getCategoryPosterminalIds from '../posterminal/getCategoryPosterminalIds.js';
|
||||
|
||||
@@ -12,19 +12,17 @@ import getCategoryPosterminalIds from '../posterminal/getCategoryPosterminalIds.
|
||||
*
|
||||
* @example
|
||||
* const fn = getTreeForCompany(pool);
|
||||
* const tree = await fn(42, 1);
|
||||
* const tree = await fn(42);
|
||||
* // => { languageId, tree: [{ id, posterminalIds, ... }], articles: [...] }
|
||||
*/
|
||||
export default function getTreeForCompany(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const articles = queryCategoryArticles(pool);
|
||||
const posterminalIds = getCategoryPosterminalIds(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
const { rootCategoryId, languageId, posCustomerGroupId } = await access(
|
||||
googleAuthId,
|
||||
companyId,
|
||||
);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId, rootCategoryId, languageId, posCustomerGroupId } =
|
||||
await access(googleAuthId);
|
||||
|
||||
const [treeResult, articleRows, ptMap] = await Promise.all([
|
||||
pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_COMPANY_AND_KEY } from '../../sql/companysetting.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a company setting by key.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 'theme');
|
||||
* await fn(42, 'theme');
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, key) => {
|
||||
return async (googleAuthId, key) => {
|
||||
if (!key?.trim()) {
|
||||
throw new Error('Setting not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_COMPANY_AND_KEY } from '../../sql/companysetting.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shapeSetting from './shapeSetting.js';
|
||||
|
||||
/**
|
||||
@@ -10,18 +10,18 @@ import shapeSetting from './shapeSetting.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = get(pool);
|
||||
* const setting = await fn(42, 1, 'theme');
|
||||
* const setting = await fn(42, 'theme');
|
||||
* // => { key, value, frontend }
|
||||
*/
|
||||
export default function get(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, key, { frontendOnly = false } = {}) => {
|
||||
return async (googleAuthId, key, { frontendOnly = false } = {}) => {
|
||||
if (!key?.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
LIST_BY_COMPANY,
|
||||
LIST_BY_COMPANY_FRONTEND,
|
||||
} from '../../sql/companysetting.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shapeSetting from './shapeSetting.js';
|
||||
|
||||
/**
|
||||
@@ -13,14 +13,14 @@ import shapeSetting from './shapeSetting.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const settings = await fn(42, 1, { frontendOnly: true });
|
||||
* const settings = await fn(42, { frontendOnly: true });
|
||||
* // => [{ key, value, frontend }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, { frontendOnly = false } = {}) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId, { frontendOnly = false } = {}) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_COMPANY_AND_KEY, UPSERT } from '../../sql/companysetting.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shapeSetting from './shapeSetting.js';
|
||||
|
||||
/**
|
||||
@@ -10,13 +10,13 @@ import shapeSetting from './shapeSetting.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = set(pool);
|
||||
* const setting = await fn(42, 1, { key: 'theme', value: 'dark', frontend: true });
|
||||
* const setting = await fn(42, { key: 'theme', value: 'dark', frontend: true });
|
||||
* // => { key, value, frontend }
|
||||
*/
|
||||
export default function set(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, input = {}) => {
|
||||
return async (googleAuthId, input = {}) => {
|
||||
const key = input.key?.trim();
|
||||
if (!key) {
|
||||
throw new Error('Invalid setting');
|
||||
@@ -26,7 +26,7 @@ export default function set(pool) {
|
||||
}
|
||||
|
||||
const frontend = Boolean(input.frontend);
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
await pool
|
||||
.request()
|
||||
|
||||
35
src/store/companyuser/resolveSelectedCompanyAccess.js
Normal file
35
src/store/companyuser/resolveSelectedCompanyAccess.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { GET_BY_ID } from '../../sql/google_auth.js';
|
||||
import resolveCompanyAccess from './resolveCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Resolves the user's selected company and checks company membership.
|
||||
* @param {object} pool
|
||||
* @returns {(googleAuthId: number) => Promise<object>}
|
||||
*/
|
||||
export default function resolveSelectedCompanyAccess(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId) => {
|
||||
if (!Number.isFinite(googleAuthId)) {
|
||||
throw new Error('Not authorized');
|
||||
}
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
.input('googleAuthId', googleAuthId)
|
||||
.query(GET_BY_ID);
|
||||
|
||||
const row = result.recordset[0];
|
||||
if (!row) {
|
||||
throw new Error('Not authorized');
|
||||
}
|
||||
|
||||
const companyId = row.selectedCompanyId;
|
||||
if (companyId == null) {
|
||||
throw new Error('No company selected');
|
||||
}
|
||||
|
||||
const ctx = await access(googleAuthId, companyId);
|
||||
return { companyId, ...ctx };
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CREATE, GET_NEXT_SORT } from '../../sql/customergroup.js';
|
||||
import { POS_GROUP_NAME, SUPPLIER_GROUP_NAME } from '../../sql/entity.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
const RESERVED_NAMES = new Set([POS_GROUP_NAME, SUPPLIER_GROUP_NAME]);
|
||||
|
||||
@@ -16,13 +16,13 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const group = await fn(42, 1, { name: 'Wholesale' });
|
||||
* const group = await fn(42, { name: 'Wholesale' });
|
||||
* // => { id, name, sort }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, { name } = {}) => {
|
||||
return async (googleAuthId, { name } = {}) => {
|
||||
const cleaned = name?.trim();
|
||||
if (!cleaned) {
|
||||
throw new Error('Name required');
|
||||
@@ -31,7 +31,7 @@ export default function create(pool) {
|
||||
throw new Error('Name not allowed');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const next = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_COMPANY_AND_ID } from '../../sql/customergroup.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a customer group for a company by its id.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 5);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Customer group not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
try {
|
||||
const result = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LIST_BY_COMPANY } from '../../sql/customergroup.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Lists the customer groups of a company.
|
||||
@@ -9,14 +9,14 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const groups = await fn(42, 1);
|
||||
* const groups = await fn(42);
|
||||
* // => [{ id, name, sort }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
UPDATE_SORT,
|
||||
} from '../../sql/customergroup.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Reorders the customer groups of a company.
|
||||
@@ -13,18 +13,18 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = order(pool);
|
||||
* const result = await fn(42, 1, { customergroupIds: [3, 1, 2] });
|
||||
* const result = await fn(42, { customergroupIds: [3, 1, 2] });
|
||||
* // => { customergroupIds: [3, 1, 2] }
|
||||
*/
|
||||
export default function order(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, { customergroupIds } = {}) => {
|
||||
return async (googleAuthId, { customergroupIds } = {}) => {
|
||||
if (!Array.isArray(customergroupIds)) {
|
||||
throw new Error('Invalid customer group order');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const groups = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GET_BY_COMPANY_AND_ID, UPDATE } from '../../sql/customergroup.js';
|
||||
import { POS_GROUP_NAME, SUPPLIER_GROUP_NAME } from '../../sql/entity.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
const LOCKED_NAMES = new Set([POS_GROUP_NAME, SUPPLIER_GROUP_NAME]);
|
||||
|
||||
@@ -16,13 +16,13 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const group = await fn(42, 1, 5, { name: 'Retail' });
|
||||
* const group = await fn(42, 5, { name: 'Retail' });
|
||||
* // => { id, name, sort }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id, { name } = {}) => {
|
||||
return async (googleAuthId, id, { name } = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Customer group not found');
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export default function update(pool) {
|
||||
throw new Error('Name required');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const existing = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CREATE_MANIFEST, DELIVERY_TYPE } from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import {
|
||||
loadDelivery,
|
||||
@@ -17,14 +17,14 @@ import { assertSupplier, insertLines } from './writeLines.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const delivery = await fn(42, 1, { number: 'D-1', date: '2026-01-01', supplierId: 5, lines: [...] });
|
||||
* const delivery = await fn(42, { number: 'D-1', date: '2026-01-01', supplierId: 5, lines: [...] });
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
|
||||
return async (googleAuthId, companyId, body = {}) => {
|
||||
return async (googleAuthId, body = {}) => {
|
||||
const number = body.number?.trim?.() ? body.number.trim() : null;
|
||||
const date = parseDeliveryDate(body.date);
|
||||
if (!number || !date) {
|
||||
@@ -33,7 +33,7 @@ export default function create(pool) {
|
||||
|
||||
const lines = normalizeLines(body.lines ?? []);
|
||||
|
||||
const { rootCategoryId } = await access(googleAuthId, companyId);
|
||||
const { companyId, rootCategoryId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_MANIFEST, DELIVERY_TYPE } from '../../sql/delivery.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a delivery by its id within a company.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 5);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Delivery not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { loadDelivery } from './helpers.js';
|
||||
|
||||
/**
|
||||
@@ -9,18 +9,18 @@ import { loadDelivery } from './helpers.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = getById(pool);
|
||||
* const delivery = await fn(42, 1, 5);
|
||||
* const delivery = await fn(42, 5);
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
export default function getById(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Delivery not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const delivery = await loadDelivery(pool, null, companyId, id);
|
||||
if (!delivery) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELIVERY_TYPE, LIST_DELIVERIES } from '../../sql/delivery.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { shapeSummary } from './helpers.js';
|
||||
|
||||
/**
|
||||
@@ -10,14 +10,14 @@ import { shapeSummary } from './helpers.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const deliveries = await fn(42, 1);
|
||||
* const deliveries = await fn(42);
|
||||
* // => [{ id, number, type, entityId, date }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
UPDATE_MANIFEST,
|
||||
} from '../../sql/delivery.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import {
|
||||
loadDelivery,
|
||||
@@ -23,14 +23,14 @@ import { assertSupplier, insertLines } from './writeLines.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const delivery = await fn(42, 1, 5, { number: 'D-2', lines: [...] });
|
||||
* const delivery = await fn(42, 5, { number: 'D-2', lines: [...] });
|
||||
* // => { id, number, type, entityId, date, lines: [...] }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id, patch = {}) => {
|
||||
return async (googleAuthId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Delivery not found');
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export default function update(pool) {
|
||||
normalizedLines = normalizeLines(patch.lines);
|
||||
}
|
||||
|
||||
const { rootCategoryId } = await access(googleAuthId, companyId);
|
||||
const { companyId, rootCategoryId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
|
||||
@@ -1,40 +1,30 @@
|
||||
import { CREATE } from '../../sql/entity.js';
|
||||
import { INSERT as INSERT_HISTORY } from '../../sql/entityhistory.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveCustomerGroupAccess from '../customergroup/resolveCustomerGroupAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
customergroupId: row.customergroupId,
|
||||
firstName: row.firstName ?? null,
|
||||
name: row.name,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
};
|
||||
}
|
||||
import nextNumber from '../numbers/nextNumber.js';
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Creates a new entity (customer) in a customer group, recording history.
|
||||
* Optional `number`; if omitted, allocates via `getnextnumberwithprefixandpadding`
|
||||
* (`type` `entity`).
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that creates an entity.
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const entity = await fn(42, 1, { customergroupId: 5, name: 'Doe', firstName: 'John' });
|
||||
* // => { id, customergroupId, firstName, name, createdAt, updatedAt }
|
||||
* const entity = await fn(42, { customergroupId: 5, name: 'Doe', firstName: 'John' });
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const groupAccess = resolveCustomerGroupAccess(pool);
|
||||
const allocateNumber = nextNumber(pool);
|
||||
|
||||
return async (
|
||||
googleAuthId,
|
||||
companyId,
|
||||
{ customergroupId, name, firstName = null } = {},
|
||||
) => {
|
||||
return async (googleAuthId, { customergroupId, name, firstName = null, number } = {}) => {
|
||||
const cleaned = name?.trim();
|
||||
if (!cleaned) {
|
||||
throw new Error('Name required');
|
||||
@@ -45,17 +35,26 @@ export default function create(pool) {
|
||||
? null
|
||||
: String(firstName).trim() || null;
|
||||
|
||||
const { companyuserId } = await access(googleAuthId, companyId);
|
||||
const explicitNumber =
|
||||
number == null || number === ''
|
||||
? null
|
||||
: String(number).trim() || null;
|
||||
|
||||
const { companyId, companyuserId } = await access(googleAuthId);
|
||||
await groupAccess(companyId, customergroupId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
|
||||
try {
|
||||
const entityNumber =
|
||||
explicitNumber ?? (await allocateNumber(companyId, 'entity', tx));
|
||||
|
||||
const created = await request(pool, tx)
|
||||
.input('customergroupId', customergroupId)
|
||||
.input('firstName', first)
|
||||
.input('name', cleaned)
|
||||
.input('number', entityNumber)
|
||||
.query(CREATE);
|
||||
|
||||
const row = created.recordset[0];
|
||||
@@ -74,6 +73,9 @@ export default function create(pool) {
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (err.number === 2601 || err.number === 2627) {
|
||||
throw new Error('Number already in use');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_COMPANY_AND_ID } from '../../sql/entity.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes an entity by its id within a company.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 5);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
import { GET_BY_COMPANY_AND_ID } from '../../sql/entity.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
customergroupId: row.customergroupId,
|
||||
firstName: row.firstName ?? null,
|
||||
name: row.name,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
};
|
||||
}
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Returns a single entity by its id within a company.
|
||||
@@ -20,18 +10,18 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = getById(pool);
|
||||
* const entity = await fn(42, 1, 5);
|
||||
* // => { id, customergroupId, firstName, name, createdAt, updatedAt }
|
||||
* const entity = await fn(42, 5);
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
export default function getById(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
46
src/store/entity/isNumberFree.js
Normal file
46
src/store/entity/isNumberFree.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { NUMBER_TAKEN_IN_COMPANY } from '../../sql/entity.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Returns whether a customer number is free within the company.
|
||||
* Pass `excludeEntityId` when editing so the entity's own number counts as free.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that checks number availability.
|
||||
*
|
||||
* @example
|
||||
* const fn = isNumberFree(pool);
|
||||
* const free = await fn(42, 'C-100');
|
||||
* // => true
|
||||
* const freeOnEdit = await fn(42, 'C-100', { excludeEntityId: 5 });
|
||||
* // => true
|
||||
*/
|
||||
export default function isNumberFree(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, number, { excludeEntityId } = {}) => {
|
||||
const cleaned =
|
||||
number == null || number === '' ? null : String(number).trim() || null;
|
||||
if (!cleaned) {
|
||||
throw new Error('Number required');
|
||||
}
|
||||
|
||||
if (
|
||||
excludeEntityId != null &&
|
||||
!Number.isFinite(excludeEntityId)
|
||||
) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
.input('companyId', companyId)
|
||||
.input('number', cleaned)
|
||||
.input('excludeEntityId', excludeEntityId ?? null)
|
||||
.query(NUMBER_TAKEN_IN_COMPANY);
|
||||
|
||||
return !result.recordset[0];
|
||||
};
|
||||
}
|
||||
@@ -5,19 +5,9 @@ import {
|
||||
POS_GROUP_NAME,
|
||||
SUPPLIER_GROUP_NAME,
|
||||
} from '../../sql/entity.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveCustomerGroupAccess from '../customergroup/resolveCustomerGroupAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
customergroupId: row.customergroupId,
|
||||
firstName: row.firstName ?? null,
|
||||
name: row.name,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
};
|
||||
}
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Lists the entities (customers) of a company, optionally filtered by customer
|
||||
@@ -28,19 +18,15 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const entities = await fn(42, 1, { customergroupId: 5 });
|
||||
* // => [{ id, customergroupId, firstName, name, createdAt, updatedAt }]
|
||||
* const entities = await fn(42, { customergroupId: 5 });
|
||||
* // => [{ id, customergroupId, firstName, name, number, createdAt, updatedAt }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const groupAccess = resolveCustomerGroupAccess(pool);
|
||||
|
||||
return async (
|
||||
googleAuthId,
|
||||
companyId,
|
||||
{ customergroupId, role } = {},
|
||||
) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId, { customergroupId, role } = {}) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
if (role != null && role !== 'customer') {
|
||||
throw new Error('Invalid role');
|
||||
|
||||
24
src/store/entity/peekNextNumber.js
Normal file
24
src/store/entity/peekNextNumber.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import peekNumber from '../numbers/peekNumber.js';
|
||||
|
||||
/**
|
||||
* Returns the next customer number that would be allocated for the company
|
||||
* (`numbers` type `entity`) without consuming it.
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that peeks the next entity number.
|
||||
*
|
||||
* @example
|
||||
* const fn = peekNextNumber(pool);
|
||||
* const number = await fn(42);
|
||||
* // => '1'
|
||||
*/
|
||||
export default function peekNextNumber(pool) {
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const peek = peekNumber(pool);
|
||||
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
return peek(companyId, 'entity');
|
||||
};
|
||||
}
|
||||
11
src/store/entity/shape.js
Normal file
11
src/store/entity/shape.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function shape(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
customergroupId: row.customergroupId,
|
||||
firstName: row.firstName ?? null,
|
||||
name: row.name,
|
||||
number: row.number ?? null,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
};
|
||||
}
|
||||
@@ -1,45 +1,37 @@
|
||||
import { GET_BY_COMPANY_AND_ID, UPDATE } from '../../sql/entity.js';
|
||||
import { INSERT as INSERT_HISTORY } from '../../sql/entityhistory.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
customergroupId: row.customergroupId,
|
||||
firstName: row.firstName ?? null,
|
||||
name: row.name,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
};
|
||||
}
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shape from './shape.js';
|
||||
|
||||
/**
|
||||
* Updates the name and/or first name of an entity, recording history.
|
||||
* Updates name, first name, and/or customer number of an entity, recording history.
|
||||
* Optional `number` sets the customer number explicitly (does not auto-allocate).
|
||||
*
|
||||
* @param {object} pool - The mssql connection pool.
|
||||
* @returns {Function} An async function that updates an entity.
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const entity = await fn(42, 1, 5, { name: 'Doe', firstName: 'Jane' });
|
||||
* // => { id, customergroupId, firstName, name, createdAt, updatedAt }
|
||||
* const entity = await fn(42, 5, { name: 'Doe', firstName: 'Jane', number: 'C-42' });
|
||||
* // => { id, customergroupId, firstName, name, number, createdAt, updatedAt }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id, patch = {}) => {
|
||||
return async (googleAuthId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Entity not found');
|
||||
}
|
||||
|
||||
const hasName = Object.prototype.hasOwnProperty.call(patch, 'name');
|
||||
const hasFirstName = Object.prototype.hasOwnProperty.call(patch, 'firstName');
|
||||
if (!hasName && !hasFirstName) {
|
||||
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
||||
if (!hasName && !hasFirstName && !hasNumber) {
|
||||
throw new Error('Name required');
|
||||
}
|
||||
|
||||
const { companyuserId } = await access(googleAuthId, companyId);
|
||||
const { companyId, companyuserId } = await access(googleAuthId);
|
||||
|
||||
const existing = await pool
|
||||
.request()
|
||||
@@ -68,6 +60,14 @@ export default function update(pool) {
|
||||
: String(patch.firstName).trim() || null;
|
||||
}
|
||||
|
||||
let number = current.number ?? null;
|
||||
if (hasNumber) {
|
||||
number =
|
||||
patch.number == null || patch.number === ''
|
||||
? null
|
||||
: String(patch.number).trim() || null;
|
||||
}
|
||||
|
||||
const historyLines = [];
|
||||
const oldFirst = current.firstName ?? '';
|
||||
const newFirst = firstName ?? '';
|
||||
@@ -81,6 +81,13 @@ export default function update(pool) {
|
||||
`Name geändert von "${current.name}" auf "${name}"`,
|
||||
);
|
||||
}
|
||||
const oldNumber = current.number ?? '';
|
||||
const newNumber = number ?? '';
|
||||
if (oldNumber !== newNumber) {
|
||||
historyLines.push(
|
||||
`Nummer geändert von "${oldNumber}" auf "${newNumber}"`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!historyLines.length) {
|
||||
return shape(current);
|
||||
@@ -94,6 +101,7 @@ export default function update(pool) {
|
||||
.input('id', id)
|
||||
.input('firstName', firstName)
|
||||
.input('name', name)
|
||||
.input('number', number)
|
||||
.query(UPDATE);
|
||||
|
||||
const row = updated.recordset[0];
|
||||
@@ -115,6 +123,9 @@ export default function update(pool) {
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (err.number === 2601 || err.number === 2627) {
|
||||
throw new Error('Number already in use');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CREATE, GET_NEXT_SORT } from '../../sql/entityaddress.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
@@ -32,14 +32,14 @@ function optionalString(value) {
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const address = await fn(42, 1, 5, { addressType: 'billing', street: 'Main St', city: 'Berlin' });
|
||||
* const address = await fn(42, 5, { addressType: 'billing', street: 'Main St', city: 'Berlin' });
|
||||
* // => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId, input = {}) => {
|
||||
return async (googleAuthId, entityId, input = {}) => {
|
||||
const addressType = input.addressType?.trim?.()
|
||||
? input.addressType.trim()
|
||||
: null;
|
||||
@@ -47,7 +47,7 @@ export default function create(pool) {
|
||||
throw new Error('Address type required');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
let sort = input.sort;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_ENTITY_AND_ID } from '../../sql/entityaddress.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
/**
|
||||
@@ -10,18 +10,18 @@ import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 5, 9);
|
||||
* await fn(42, 5, 9);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId, id) => {
|
||||
return async (googleAuthId, entityId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Address not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
const result = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LIST_BY_ENTITY } from '../../sql/entityaddress.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
@@ -26,15 +26,15 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const addresses = await fn(42, 1, 5);
|
||||
* const addresses = await fn(42, 5);
|
||||
* // => [{ id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId, entityId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
const result = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_ENTITY_AND_ID, UPDATE } from '../../sql/entityaddress.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
@@ -32,19 +32,19 @@ function optionalString(value) {
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const address = await fn(42, 1, 5, 9, { city: 'Hamburg' });
|
||||
* const address = await fn(42, 5, 9, { city: 'Hamburg' });
|
||||
* // => { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType, sort }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId, id, patch = {}) => {
|
||||
return async (googleAuthId, entityId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Address not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
const existing = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CREATE, GET_NEXT_SORT } from '../../sql/entitycontact.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
@@ -20,14 +20,14 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const contact = await fn(42, 1, 5, { contactType: 'email', contact: 'a@b.c' });
|
||||
* const contact = await fn(42, 5, { contactType: 'email', contact: 'a@b.c' });
|
||||
* // => { id, entityId, contactType, contact, sort }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId, input = {}) => {
|
||||
return async (googleAuthId, entityId, input = {}) => {
|
||||
const contactType = input.contactType?.trim?.()
|
||||
? input.contactType.trim()
|
||||
: null;
|
||||
@@ -36,7 +36,7 @@ export default function create(pool) {
|
||||
throw new Error('Contact required');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
let sort = input.sort;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_ENTITY_AND_ID } from '../../sql/entitycontact.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
/**
|
||||
@@ -10,18 +10,18 @@ import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 5, 9);
|
||||
* await fn(42, 5, 9);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId, id) => {
|
||||
return async (googleAuthId, entityId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Contact not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
const result = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LIST_BY_ENTITY } from '../../sql/entitycontact.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
@@ -20,15 +20,15 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const contacts = await fn(42, 1, 5);
|
||||
* const contacts = await fn(42, 5);
|
||||
* // => [{ id, entityId, contactType, contact, sort }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId, entityId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
const result = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_ENTITY_AND_ID, UPDATE } from '../../sql/entitycontact.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import resolveEntityAccess from '../entity/resolveEntityAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
@@ -20,19 +20,19 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const contact = await fn(42, 1, 5, 9, { contact: 'b@c.d' });
|
||||
* const contact = await fn(42, 5, 9, { contact: 'b@c.d' });
|
||||
* // => { id, entityId, contactType, contact, sort }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const entityAccess = resolveEntityAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, entityId, id, patch = {}) => {
|
||||
return async (googleAuthId, entityId, id, patch = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Contact not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
await entityAccess(companyId, entityId);
|
||||
|
||||
const existing = await pool
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
SUPPLIER_GROUP_NAME,
|
||||
} from '../../sql/entity.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
|
||||
function shapeSummary(type, row) {
|
||||
@@ -179,7 +179,7 @@ async function loadDoc(pool, tx, { type, companyId, id }) {
|
||||
* `label` → error prefix (`Quote` / `Order` → "Quote not found")
|
||||
*/
|
||||
export default function createManifestDocStore(pool, { type, label }) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
const ensureTax = taxEnsure(pool);
|
||||
const notFound = `${label} not found`;
|
||||
const invalid = `Invalid ${label.toLowerCase()}`;
|
||||
@@ -188,11 +188,10 @@ export default function createManifestDocStore(pool, { type, label }) {
|
||||
* Lists the quote/order documents of a company.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} companyId - The company id.
|
||||
* @returns {Promise<Array<object>>} The document summaries.
|
||||
*/
|
||||
async function list(googleAuthId, companyId) {
|
||||
await access(googleAuthId, companyId);
|
||||
async function list(googleAuthId) {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
@@ -207,16 +206,15 @@ export default function createManifestDocStore(pool, { type, label }) {
|
||||
* Returns a single quote/order document by its id within a company.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} companyId - The company id.
|
||||
* @param {number} id - The document id.
|
||||
* @returns {Promise<object|null>} The document with its lines, or null if not found.
|
||||
*/
|
||||
async function getById(googleAuthId, companyId, id) {
|
||||
async function getById(googleAuthId, id) {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const doc = await loadDoc(pool, null, { type, companyId, id });
|
||||
if (!doc) {
|
||||
@@ -229,11 +227,10 @@ export default function createManifestDocStore(pool, { type, label }) {
|
||||
* Creates a new quote/order document with its lines.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} companyId - The company id.
|
||||
* @param {object} body - The document payload (`number`, `date`, `entityId`, `lines`).
|
||||
* @returns {Promise<object>} The created document with its lines.
|
||||
*/
|
||||
async function create(googleAuthId, companyId, body = {}) {
|
||||
async function create(googleAuthId, body = {}) {
|
||||
const number = body.number?.trim?.() ? body.number.trim() : null;
|
||||
const date = parseDate(body.date);
|
||||
if (!number || !date) {
|
||||
@@ -241,7 +238,7 @@ export default function createManifestDocStore(pool, { type, label }) {
|
||||
}
|
||||
|
||||
const lines = normalizeLines(body.lines ?? []);
|
||||
const { rootCategoryId } = await access(googleAuthId, companyId);
|
||||
const { companyId, rootCategoryId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
@@ -288,12 +285,11 @@ export default function createManifestDocStore(pool, { type, label }) {
|
||||
* Updates a quote/order document's header and/or lines.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} companyId - The company id.
|
||||
* @param {number} id - The document id.
|
||||
* @param {object} patch - The fields to update (`number`, `date`, `entityId`, `lines`).
|
||||
* @returns {Promise<object>} The updated document with its lines.
|
||||
*/
|
||||
async function update(googleAuthId, companyId, id, patch = {}) {
|
||||
async function update(googleAuthId, id, patch = {}) {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
@@ -312,7 +308,7 @@ export default function createManifestDocStore(pool, { type, label }) {
|
||||
normalizedLines = normalizeLines(patch.lines);
|
||||
}
|
||||
|
||||
const { rootCategoryId } = await access(googleAuthId, companyId);
|
||||
const { companyId, rootCategoryId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
@@ -396,16 +392,15 @@ export default function createManifestDocStore(pool, { type, label }) {
|
||||
* Deletes a quote/order document by its id within a company.
|
||||
*
|
||||
* @param {number} googleAuthId - The Google auth user id.
|
||||
* @param {number} companyId - The company id.
|
||||
* @param {number} id - The document id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function del(googleAuthId, companyId, id) {
|
||||
async function del(googleAuthId, id) {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error(notFound);
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
19
src/store/numbers/nextNumber.js
Normal file
19
src/store/numbers/nextNumber.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ENSURE_TYPE } from '../../sql/numbers.js';
|
||||
import request from '../request.js';
|
||||
|
||||
export default function nextNumber(pool) {
|
||||
return async (companyId, type, tx) => {
|
||||
await request(pool, tx)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(ENSURE_TYPE);
|
||||
|
||||
const req = request(pool, tx);
|
||||
req.input('company_id', companyId);
|
||||
req.input('type', type);
|
||||
req.output('value');
|
||||
|
||||
const result = await req.execute('getnextnumberwithprefixandpadding');
|
||||
return result.output.value;
|
||||
};
|
||||
}
|
||||
@@ -1,21 +1,6 @@
|
||||
import { ENSURE_TYPE } from '../../sql/numbers.js';
|
||||
import request from '../request.js';
|
||||
import nextNumber from './nextNumber.js';
|
||||
|
||||
export default function nextSku(pool) {
|
||||
return async (companyId, tx) => {
|
||||
const type = 'sku';
|
||||
|
||||
await request(pool, tx)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(ENSURE_TYPE);
|
||||
|
||||
const req = request(pool, tx);
|
||||
req.input('company_id', companyId);
|
||||
req.input('type', type);
|
||||
req.output('value');
|
||||
|
||||
const result = await req.execute('getnextnumberwithprefixandpadding');
|
||||
return result.output.value;
|
||||
};
|
||||
const allocate = nextNumber(pool);
|
||||
return (companyId, tx) => allocate(companyId, 'sku', tx);
|
||||
}
|
||||
|
||||
19
src/store/numbers/peekNumber.js
Normal file
19
src/store/numbers/peekNumber.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ENSURE_TYPE } from '../../sql/numbers.js';
|
||||
import request from '../request.js';
|
||||
|
||||
export default function peekNumber(pool) {
|
||||
return async (companyId, type, tx) => {
|
||||
await request(pool, tx)
|
||||
.input('companyId', companyId)
|
||||
.input('type', type)
|
||||
.query(ENSURE_TYPE);
|
||||
|
||||
const req = request(pool, tx);
|
||||
req.input('company_id', companyId);
|
||||
req.input('type', type);
|
||||
req.output('value');
|
||||
|
||||
const result = await req.execute('peeknextnumberwithprefixandpadding');
|
||||
return result.output.value;
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import createManifestDocStore from '../manifestDoc/createStore.js';
|
||||
*
|
||||
* @example
|
||||
* const store = createOrderStore(pool);
|
||||
* const orders = await store.list(42, 1);
|
||||
* const orders = await store.list(42);
|
||||
* // => [{ id, number, type, entityId, date }]
|
||||
*/
|
||||
export default function createOrderStore(pool) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_COMPANY_AND_ID } from '../../sql/posterminal.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a posterminal by its id within a company.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 5);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Posterminal not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LIST_BY_COMPANY } from '../../sql/posterminal.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Lists the posterminals of a company.
|
||||
@@ -9,14 +9,14 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const terminals = await fn(42, 1);
|
||||
* const terminals = await fn(42);
|
||||
* // => [{ id, name, token, pairingpin }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_ID, SET_OFF, LIST_CATEGORY_IDS } from '../../sql/posterminal.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Removes a category assignment from a posterminal (turns it off).
|
||||
@@ -9,18 +9,18 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = setOff(pool);
|
||||
* const result = await fn(42, 1, 5, 9);
|
||||
* const result = await fn(42, 5, 9);
|
||||
* // => { id, categoryIds: [...] }
|
||||
*/
|
||||
export default function setOff(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, posterminalId, categoryId) => {
|
||||
return async (googleAuthId, posterminalId, categoryId) => {
|
||||
if (!Number.isFinite(posterminalId) || !Number.isFinite(categoryId)) {
|
||||
throw new Error('Invalid posterminal or category');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const existing = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_ID, SET_OFF_ALL, LIST_CATEGORY_IDS } from '../../sql/posterminal.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Removes all category assignments from a posterminal.
|
||||
@@ -9,18 +9,18 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = setOffAll(pool);
|
||||
* const result = await fn(42, 1, 5);
|
||||
* const result = await fn(42, 5);
|
||||
* // => { id, categoryIds: [] }
|
||||
*/
|
||||
export default function setOffAll(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, posterminalId) => {
|
||||
return async (googleAuthId, posterminalId) => {
|
||||
if (!Number.isFinite(posterminalId)) {
|
||||
throw new Error('Invalid posterminal');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
// Verify posterminal belongs to company
|
||||
const pt = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_ID, SET_ON, LIST_CATEGORY_IDS } from '../../sql/posterminal.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Assigns a category to a posterminal (turns it on for that category).
|
||||
@@ -9,18 +9,18 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = setOn(pool);
|
||||
* const result = await fn(42, 1, 5, 9);
|
||||
* const result = await fn(42, 5, 9);
|
||||
* // => { id, categoryIds: [9, ...] }
|
||||
*/
|
||||
export default function setOn(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, posterminalId, categoryId) => {
|
||||
return async (googleAuthId, posterminalId, categoryId) => {
|
||||
if (!Number.isFinite(posterminalId) || !Number.isFinite(categoryId)) {
|
||||
throw new Error('Invalid posterminal or category');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const existing = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_ID, SET_ON_ALL, LIST_CATEGORY_IDS } from '../../sql/posterminal.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { GET_COMPANY_ROOT_CATEGORY } from '../../sql/company.js';
|
||||
|
||||
/**
|
||||
@@ -10,18 +10,18 @@ import { GET_COMPANY_ROOT_CATEGORY } from '../../sql/company.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = setOnAll(pool);
|
||||
* const result = await fn(42, 1, 5);
|
||||
* const result = await fn(42, 5);
|
||||
* // => { id, categoryIds: [...] }
|
||||
*/
|
||||
export default function setOnAll(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, posterminalId) => {
|
||||
return async (googleAuthId, posterminalId) => {
|
||||
if (!Number.isFinite(posterminalId)) {
|
||||
throw new Error('Invalid posterminal');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
// Verify posterminal belongs to company
|
||||
const pt = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GET_BY_ID, CREATE, UPDATE } from '../../sql/posterminal.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Creates or updates a posterminal for a company.
|
||||
@@ -9,13 +9,13 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = upsert(pool);
|
||||
* const terminal = await fn(42, 1, { name: 'Kasse 1', pairingpin: '1234', token: 'abc' });
|
||||
* const terminal = await fn(42, { name: 'Kasse 1', pairingpin: '1234', token: 'abc' });
|
||||
* // => { id, name, token, pairingpin }
|
||||
*/
|
||||
export default function upsert(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, input = {}) => {
|
||||
return async (googleAuthId, input = {}) => {
|
||||
const name = input.name?.trim();
|
||||
if (!name) {
|
||||
throw new Error('Invalid posterminal');
|
||||
@@ -27,7 +27,7 @@ export default function upsert(pool) {
|
||||
const token = input.token ?? null;
|
||||
const id = input.id ?? null;
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
if (id) {
|
||||
const existing = await pool
|
||||
|
||||
@@ -10,7 +10,7 @@ import createManifestDocStore from '../manifestDoc/createStore.js';
|
||||
*
|
||||
* @example
|
||||
* const store = createQuoteStore(pool);
|
||||
* const quotes = await store.list(42, 1);
|
||||
* const quotes = await store.list(42);
|
||||
* // => [{ id, number, type, entityId, date }]
|
||||
*/
|
||||
export default function createQuoteStore(pool) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CREATE } from '../../sql/entity.js';
|
||||
import { INSERT as INSERT_HISTORY } from '../../sql/entityhistory.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import ensureLieferantenGroup from './ensureLieferantenGroup.js';
|
||||
|
||||
/**
|
||||
@@ -12,19 +12,19 @@ import ensureLieferantenGroup from './ensureLieferantenGroup.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = create(pool);
|
||||
* const supplier = await fn(42, 1, { name: 'Acme Supplies' });
|
||||
* const supplier = await fn(42, { name: 'Acme Supplies' });
|
||||
* // => { id, name, createdAt, updatedAt }
|
||||
*/
|
||||
export default function create(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, { name } = {}) => {
|
||||
return async (googleAuthId, { name } = {}) => {
|
||||
const cleaned = name?.trim();
|
||||
if (!cleaned) {
|
||||
throw new Error('Name required');
|
||||
}
|
||||
|
||||
const { companyuserId } = await access(googleAuthId, companyId);
|
||||
const { companyId, companyuserId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
@@ -36,6 +36,7 @@ export default function create(pool) {
|
||||
.input('customergroupId', customergroupId)
|
||||
.input('firstName', null)
|
||||
.input('name', cleaned)
|
||||
.input('number', null)
|
||||
.query(CREATE);
|
||||
|
||||
const row = created.recordset[0];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_ID, SUPPLIER_GROUP_NAME } from '../../sql/entity.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a supplier by its id within a company.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 5);
|
||||
* await fn(42, 5);
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id) => {
|
||||
return async (googleAuthId, id) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Supplier not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LIST_SUPPLIERS_BY_COMPANY, SUPPLIER_GROUP_NAME } from '../../sql/entity.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Lists the suppliers of a company.
|
||||
@@ -9,14 +9,14 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const suppliers = await fn(42, 1);
|
||||
* const suppliers = await fn(42);
|
||||
* // => [{ id, name, createdAt, updatedAt }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
await access(googleAuthId, companyId);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '../../sql/entity.js';
|
||||
import { INSERT as INSERT_HISTORY } from '../../sql/entityhistory.js';
|
||||
import request from '../request.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
function shape(row) {
|
||||
return {
|
||||
@@ -25,13 +25,13 @@ function shape(row) {
|
||||
*
|
||||
* @example
|
||||
* const fn = update(pool);
|
||||
* const supplier = await fn(42, 1, 5, { name: 'Acme GmbH' });
|
||||
* const supplier = await fn(42, 5, { name: 'Acme GmbH' });
|
||||
* // => { id, name, createdAt, updatedAt }
|
||||
*/
|
||||
export default function update(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, id, { name } = {}) => {
|
||||
return async (googleAuthId, id, { name } = {}) => {
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error('Supplier not found');
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export default function update(pool) {
|
||||
throw new Error('Name required');
|
||||
}
|
||||
|
||||
const { companyuserId } = await access(googleAuthId, companyId);
|
||||
const { companyId, companyuserId } = await access(googleAuthId);
|
||||
|
||||
const existing = await pool
|
||||
.request()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DELETE_BY_COMPANY_AND_SI } from '../../sql/unit.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
|
||||
/**
|
||||
* Deletes a unit for a company by its SI code.
|
||||
@@ -9,17 +9,17 @@ import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = del(pool);
|
||||
* await fn(42, 1, 'l');
|
||||
* await fn(42, 'l');
|
||||
*/
|
||||
export default function del(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, si) => {
|
||||
return async (googleAuthId, si) => {
|
||||
if (!si?.trim()) {
|
||||
throw new Error('Unit not found');
|
||||
}
|
||||
|
||||
await access(googleAuthId, companyId);
|
||||
const { companyId } = await access(googleAuthId);
|
||||
|
||||
try {
|
||||
const result = await pool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LIST_BY_COMPANY } from '../../sql/unit.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import shapeUnit from './shapeUnit.js';
|
||||
|
||||
/**
|
||||
@@ -10,14 +10,14 @@ import shapeUnit from './shapeUnit.js';
|
||||
*
|
||||
* @example
|
||||
* const fn = list(pool);
|
||||
* const units = await fn(42, 1);
|
||||
* const units = await fn(42);
|
||||
* // => [{ id, si, basesi, basesifactor, symbol, name }]
|
||||
*/
|
||||
export default function list(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId) => {
|
||||
const { languageId } = await access(googleAuthId, companyId);
|
||||
return async (googleAuthId) => {
|
||||
const { companyId, languageId } = await access(googleAuthId);
|
||||
|
||||
const result = await pool
|
||||
.request()
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
GET_BY_UNIT_AND_LANGUAGE,
|
||||
UPDATE_BY_UNIT_AND_LANGUAGE,
|
||||
} from '../../sql/unittext.js';
|
||||
import resolveCompanyAccess from '../companyuser/resolveCompanyAccess.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import request from '../request.js';
|
||||
import shapeUnit from './shapeUnit.js';
|
||||
|
||||
@@ -38,15 +38,15 @@ function validateUnit({ si, basesi, basesifactor, symbol, name }) {
|
||||
*
|
||||
* @example
|
||||
* const fn = upsert(pool);
|
||||
* const unit = await fn(42, 1, { si: 'l', basesi: 'm3', basesifactor: 0.001, symbol: 'L', name: 'Liter' });
|
||||
* const unit = await fn(42, { si: 'l', basesi: 'm3', basesifactor: 0.001, symbol: 'L', name: 'Liter' });
|
||||
* // => { id, si, basesi, basesifactor, symbol, name }
|
||||
*/
|
||||
export default function upsert(pool) {
|
||||
const access = resolveCompanyAccess(pool);
|
||||
const access = resolveSelectedCompanyAccess(pool);
|
||||
|
||||
return async (googleAuthId, companyId, input) => {
|
||||
return async (googleAuthId, input) => {
|
||||
const unit = validateUnit(input);
|
||||
const { languageId } = await access(googleAuthId, companyId);
|
||||
const { companyId, languageId } = await access(googleAuthId);
|
||||
|
||||
const tx = pool.transaction();
|
||||
await tx.begin();
|
||||
|
||||
Reference in New Issue
Block a user