@things-factory/sales 10.1.243 → 10.1.245
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/close-months.d.ts +5 -0
- package/dist-client/close-months.js +20 -0
- package/dist-client/close-months.js.map +1 -0
- package/dist-client/headroom-choice.d.ts +10 -0
- package/dist-client/headroom-choice.js +22 -0
- package/dist-client/headroom-choice.js.map +1 -0
- package/dist-client/pages/opportunity-list.d.ts +18 -3
- package/dist-client/pages/opportunity-list.js +93 -9
- package/dist-client/pages/opportunity-list.js.map +1 -1
- package/dist-client/pages/quotation-list.d.ts +11 -0
- package/dist-client/pages/quotation-list.js +71 -5
- package/dist-client/pages/quotation-list.js.map +1 -1
- package/dist-client/pages/sales-product-list.d.ts +6 -1
- package/dist-client/pages/sales-product-list.js +42 -2
- package/dist-client/pages/sales-product-list.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/opportunity/opportunity-query.d.ts +6 -0
- package/dist-server/service/opportunity/opportunity-query.js +14 -1
- package/dist-server/service/opportunity/opportunity-query.js.map +1 -1
- package/dist-server/service/opportunity/opportunity-service.d.ts +13 -1
- package/dist-server/service/opportunity/opportunity-service.js +13 -0
- package/dist-server/service/opportunity/opportunity-service.js.map +1 -1
- package/dist-server/service/opportunity/opportunity-type.d.ts +5 -0
- package/dist-server/service/opportunity/opportunity-type.js +19 -1
- package/dist-server/service/opportunity/opportunity-type.js.map +1 -1
- package/dist-server/service/quotation/quotation-query.d.ts +4 -0
- package/dist-server/service/quotation/quotation-query.js +13 -1
- package/dist-server/service/quotation/quotation-query.js.map +1 -1
- package/dist-server/service/quotation/quotation-service.d.ts +9 -2
- package/dist-server/service/quotation/quotation-service.js +9 -0
- package/dist-server/service/quotation/quotation-service.js.map +1 -1
- package/dist-server/service/sales-product/sales-product-query.d.ts +4 -0
- package/dist-server/service/sales-product/sales-product-query.js +14 -1
- package/dist-server/service/sales-product/sales-product-query.js.map +1 -1
- package/dist-server/service/sales-product/sales-product-service.d.ts +11 -1
- package/dist-server/service/sales-product/sales-product-service.js +16 -0
- package/dist-server/service/sales-product/sales-product-service.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/translations/en.json +8 -0
- package/translations/ko.json +8 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sales-product-query.js","sourceRoot":"","sources":["../../../server/service/sales-product/sales-product-query.ts"],"names":[],"mappings":";;;;AAAA,yDAAgD;AAChD,
|
|
1
|
+
{"version":3,"file":"sales-product-query.js","sourceRoot":"","sources":["../../../server/service/sales-product/sales-product-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, ListValueCount, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { SalesProduct } from './sales-product'\nimport { PRODUCT_SEARCHABLES, salesProductActiveCounts } from './sales-product-service'\nimport { SalesProductList } from './sales-product-type'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(SalesProduct)\nexport class SalesProductQuery {\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => SalesProduct, { nullable: true, description: 'To fetch a sales product' })\n salesProduct(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n return getRepository(SalesProduct).findOne({ where: { id, domain: { id: context.state.domain.id } } })\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => SalesProductList, { description: 'To fetch a page of sales products' })\n async salesProducts(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<SalesProductList> {\n const query = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(SalesProduct),\n searchables: PRODUCT_SEARCHABLES\n })\n const [items, total] = await query.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"sales\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [ListValueCount], { description: 'To count the products in use (active) and out of use (inactive), under the list conditions other than active' })\n salesProductActiveCounts(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext) {\n return salesProductActiveCounts(getRepository(SalesProduct).manager, context.state.domain, params)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: SalesProduct) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n creator(@Root() row: SalesProduct) {\n return row.creator ?? (row.creatorId ? getRepository(User).findOneBy({ id: row.creatorId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: SalesProduct) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Domain } from '@things-factory/shell';
|
|
1
|
+
import { Domain, ListParam } from '@things-factory/shell';
|
|
2
2
|
import { EntityManager } from 'typeorm';
|
|
3
3
|
import { SalesProduct } from './sales-product';
|
|
4
4
|
import { SalesProductPatch } from './sales-product-type';
|
|
@@ -8,3 +8,13 @@ import { SalesProductPatch } from './sales-product-type';
|
|
|
8
8
|
* product is deleted freely.
|
|
9
9
|
*/
|
|
10
10
|
export declare function saveSalesProducts(tx: EntityManager, domain: Domain, user: any, patches: SalesProductPatch[]): Promise<SalesProduct[]>;
|
|
11
|
+
/** What the product list finds products by, and its in-use counts alike. */
|
|
12
|
+
export declare const PRODUCT_SEARCHABLES: string[];
|
|
13
|
+
/**
|
|
14
|
+
* How many products are in use and out of use, under the list's other conditions - the numbers beside the list's
|
|
15
|
+
* `active` and `inactive` chips. The database gives a boolean as it keeps it (true or 1), read here as one.
|
|
16
|
+
*/
|
|
17
|
+
export declare function salesProductActiveCounts(tx: EntityManager, domain: Domain, params: ListParam): Promise<{
|
|
18
|
+
value: string;
|
|
19
|
+
count: number;
|
|
20
|
+
}[]>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRODUCT_SEARCHABLES = void 0;
|
|
3
4
|
exports.saveSalesProducts = saveSalesProducts;
|
|
5
|
+
exports.salesProductActiveCounts = salesProductActiveCounts;
|
|
4
6
|
const invoice_1 = require("@things-factory/invoice");
|
|
5
7
|
const shell_1 = require("@things-factory/shell");
|
|
6
8
|
const typeorm_1 = require("typeorm");
|
|
@@ -86,4 +88,18 @@ async function saveSalesProducts(tx, domain, user, patches) {
|
|
|
86
88
|
saved.push(await repository.save({ ...row, domain, ...(row.id ? {} : { creator: user }), updater: user }));
|
|
87
89
|
return saved;
|
|
88
90
|
}
|
|
91
|
+
/** What the product list finds products by, and its in-use counts alike. */
|
|
92
|
+
exports.PRODUCT_SEARCHABLES = ['code', 'name', 'grade', 'spec', 'description'];
|
|
93
|
+
/**
|
|
94
|
+
* How many products are in use and out of use, under the list's other conditions - the numbers beside the list's
|
|
95
|
+
* `active` and `inactive` chips. The database gives a boolean as it keeps it (true or 1), read here as one.
|
|
96
|
+
*/
|
|
97
|
+
async function salesProductActiveCounts(tx, domain, params) {
|
|
98
|
+
const counted = await (0, shell_1.countListByField)({ domain, params, repository: tx.getRepository(sales_product_1.SalesProduct), searchables: exports.PRODUCT_SEARCHABLES, field: 'active' });
|
|
99
|
+
const of = (on) => [...counted].filter(([value]) => (value === 'true' || value === '1') === on).reduce((sum, [, count]) => sum + count, 0);
|
|
100
|
+
return [
|
|
101
|
+
{ value: 'active', count: of(true) },
|
|
102
|
+
{ value: 'inactive', count: of(false) }
|
|
103
|
+
];
|
|
104
|
+
}
|
|
89
105
|
//# sourceMappingURL=sales-product-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sales-product-service.js","sourceRoot":"","sources":["../../../server/service/sales-product/sales-product-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { currencyDigits } from '@things-factory/invoice'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { EntityManager, In } from 'typeorm'\n\nimport { SalesProduct } from './sales-product'\nimport { SalesProductKind, unitOf } from './sales-product-rules'\nimport { SalesProductPatch } from './sales-product-type'\n\nconst TEXT = ['grade', 'spec', 'description'] as const\n\n/** A patch as the row it writes: words trimmed, the unit the kind's when blank, the price and currency checked. */\nfunction rowOf(patch: SalesProductPatch, old?: SalesProduct): Partial<SalesProduct> {\n const row: Record<string, any> = {}\n if (patch.code !== undefined || !old) {\n const code = String(patch.code ?? '').trim()\n if (!code) throw new Refusal('sales-product-code-required', {}, 'Give the product a code')\n row.code = code\n }\n if (patch.name !== undefined || !old) {\n const name = String(patch.name ?? '').trim()\n if (!name) throw new Refusal('sales-product-name-required', {}, 'Name the product')\n row.name = name\n }\n const kind = patch.kind ?? old?.kind ?? SalesProductKind.GOODS\n row.kind = kind\n for (const field of TEXT) if (patch[field] !== undefined) row[field] = String(patch[field] ?? '').trim() || null\n /* A blank unit is the kind's - also when the kind changes and the unit was the old kind's own. */\n if (patch.unit !== undefined || !old) row.unit = unitOf(kind, patch.unit)\n else if (patch.kind !== undefined) row.unit = old.unit === unitOf(old.kind) ? unitOf(kind) : old.unit\n if (patch.currency !== undefined || !old) {\n row.currency = (String(patch.currency ?? '').trim() || 'KRW').toUpperCase()\n currencyDigits(row.currency)\n }\n if (patch.unitPrice !== undefined || !old) {\n const price = patch.unitPrice ?? 0\n if (!Number.isFinite(price) || price < 0) throw new Refusal('sales-product-price-invalid', {}, 'The price is zero or more')\n /* A quotation line takes a price to four decimals (invoice's document-amounts) - a finer one would be refused there. */\n if (Math.abs(price * 10_000 - Math.round(price * 10_000)) > 1e-6)\n throw new Refusal('sales-product-price-decimals', { value: String(price), max: 4 }, 'unitPrice: at most four decimals')\n row.unitPrice = price\n }\n if (patch.taxKind !== undefined || !old) row.taxKind = patch.taxKind ?? 'TAXABLE'\n if (patch.active !== undefined || !old) row.active = patch.active !== false\n return row\n}\n\n/**\n * Apply grid rows to a domain's products: `+` (or no id) registers, `M` changes, `-` deletes (softly). A code is the\n * domain's once among the products not deleted. Nothing names a product - a quotation line copies what it says - so a\n * product is deleted freely.\n */\nexport async function saveSalesProducts(tx: EntityManager, domain: Domain, user: any, patches: SalesProductPatch[]) {\n const repository = tx.getRepository(SalesProduct)\n const deletes = patches.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!)\n const changes = patches.filter(one => one.cuFlag !== '-')\n\n if (deletes.length) {\n const owned = await repository.find({ where: { domain: { id: domain.id }, id: In(deletes) }, select: ['id'] })\n if (owned.length !== deletes.length) throw new Refusal('sales-product-not-found', {}, 'A product was not found - reload and try again')\n await repository.softDelete({ id: In(deletes) })\n }\n\n const rows: Partial<SalesProduct>[] = []\n for (const patch of changes) {\n const old = patch.id && patch.cuFlag !== '+' ? await repository.findOne({ where: { domain: { id: domain.id }, id: patch.id } }) : undefined\n if (patch.id && patch.cuFlag !== '+' && !old) throw new Refusal('sales-product-not-found', {}, 'A product was not found - reload and try again')\n rows.push({ ...(old ?? {}), ...rowOf(patch, old ?? undefined) })\n }\n\n /* One code, one product: against the rows kept and against one another in the batch. */\n const codes = rows.map(row => row.code!)\n const taken = codes.length ? await repository.find({ where: { domain: { id: domain.id }, code: In(codes) }, select: ['id', 'code'] }) : []\n for (const [index, row] of rows.entries()) {\n const clash = taken.find(one => one.code === row.code && one.id !== row.id) || rows.find((other, at) => at < index && other.code === row.code)\n if (clash) throw new Refusal('sales-product-code-duplicate', { code: row.code }, `Product code ${row.code} is already in use`)\n }\n\n const saved: SalesProduct[] = []\n for (const row of rows) saved.push(await repository.save({ ...row, domain, ...(row.id ? {} : { creator: user }), updater: user } as SalesProduct))\n return saved\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sales-product-service.js","sourceRoot":"","sources":["../../../server/service/sales-product/sales-product-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { currencyDigits } from '@things-factory/invoice'\nimport { countListByField, Domain, ListParam, Refusal } from '@things-factory/shell'\nimport { EntityManager, In } from 'typeorm'\n\nimport { SalesProduct } from './sales-product'\nimport { SalesProductKind, unitOf } from './sales-product-rules'\nimport { SalesProductPatch } from './sales-product-type'\n\nconst TEXT = ['grade', 'spec', 'description'] as const\n\n/** A patch as the row it writes: words trimmed, the unit the kind's when blank, the price and currency checked. */\nfunction rowOf(patch: SalesProductPatch, old?: SalesProduct): Partial<SalesProduct> {\n const row: Record<string, any> = {}\n if (patch.code !== undefined || !old) {\n const code = String(patch.code ?? '').trim()\n if (!code) throw new Refusal('sales-product-code-required', {}, 'Give the product a code')\n row.code = code\n }\n if (patch.name !== undefined || !old) {\n const name = String(patch.name ?? '').trim()\n if (!name) throw new Refusal('sales-product-name-required', {}, 'Name the product')\n row.name = name\n }\n const kind = patch.kind ?? old?.kind ?? SalesProductKind.GOODS\n row.kind = kind\n for (const field of TEXT) if (patch[field] !== undefined) row[field] = String(patch[field] ?? '').trim() || null\n /* A blank unit is the kind's - also when the kind changes and the unit was the old kind's own. */\n if (patch.unit !== undefined || !old) row.unit = unitOf(kind, patch.unit)\n else if (patch.kind !== undefined) row.unit = old.unit === unitOf(old.kind) ? unitOf(kind) : old.unit\n if (patch.currency !== undefined || !old) {\n row.currency = (String(patch.currency ?? '').trim() || 'KRW').toUpperCase()\n currencyDigits(row.currency)\n }\n if (patch.unitPrice !== undefined || !old) {\n const price = patch.unitPrice ?? 0\n if (!Number.isFinite(price) || price < 0) throw new Refusal('sales-product-price-invalid', {}, 'The price is zero or more')\n /* A quotation line takes a price to four decimals (invoice's document-amounts) - a finer one would be refused there. */\n if (Math.abs(price * 10_000 - Math.round(price * 10_000)) > 1e-6)\n throw new Refusal('sales-product-price-decimals', { value: String(price), max: 4 }, 'unitPrice: at most four decimals')\n row.unitPrice = price\n }\n if (patch.taxKind !== undefined || !old) row.taxKind = patch.taxKind ?? 'TAXABLE'\n if (patch.active !== undefined || !old) row.active = patch.active !== false\n return row\n}\n\n/**\n * Apply grid rows to a domain's products: `+` (or no id) registers, `M` changes, `-` deletes (softly). A code is the\n * domain's once among the products not deleted. Nothing names a product - a quotation line copies what it says - so a\n * product is deleted freely.\n */\nexport async function saveSalesProducts(tx: EntityManager, domain: Domain, user: any, patches: SalesProductPatch[]) {\n const repository = tx.getRepository(SalesProduct)\n const deletes = patches.filter(one => one.cuFlag === '-' && one.id).map(one => one.id!)\n const changes = patches.filter(one => one.cuFlag !== '-')\n\n if (deletes.length) {\n const owned = await repository.find({ where: { domain: { id: domain.id }, id: In(deletes) }, select: ['id'] })\n if (owned.length !== deletes.length) throw new Refusal('sales-product-not-found', {}, 'A product was not found - reload and try again')\n await repository.softDelete({ id: In(deletes) })\n }\n\n const rows: Partial<SalesProduct>[] = []\n for (const patch of changes) {\n const old = patch.id && patch.cuFlag !== '+' ? await repository.findOne({ where: { domain: { id: domain.id }, id: patch.id } }) : undefined\n if (patch.id && patch.cuFlag !== '+' && !old) throw new Refusal('sales-product-not-found', {}, 'A product was not found - reload and try again')\n rows.push({ ...(old ?? {}), ...rowOf(patch, old ?? undefined) })\n }\n\n /* One code, one product: against the rows kept and against one another in the batch. */\n const codes = rows.map(row => row.code!)\n const taken = codes.length ? await repository.find({ where: { domain: { id: domain.id }, code: In(codes) }, select: ['id', 'code'] }) : []\n for (const [index, row] of rows.entries()) {\n const clash = taken.find(one => one.code === row.code && one.id !== row.id) || rows.find((other, at) => at < index && other.code === row.code)\n if (clash) throw new Refusal('sales-product-code-duplicate', { code: row.code }, `Product code ${row.code} is already in use`)\n }\n\n const saved: SalesProduct[] = []\n for (const row of rows) saved.push(await repository.save({ ...row, domain, ...(row.id ? {} : { creator: user }), updater: user } as SalesProduct))\n return saved\n}\n\n/** What the product list finds products by, and its in-use counts alike. */\nexport const PRODUCT_SEARCHABLES = ['code', 'name', 'grade', 'spec', 'description']\n\n/**\n * How many products are in use and out of use, under the list's other conditions - the numbers beside the list's\n * `active` and `inactive` chips. The database gives a boolean as it keeps it (true or 1), read here as one.\n */\nexport async function salesProductActiveCounts(tx: EntityManager, domain: Domain, params: ListParam) {\n const counted = await countListByField({ domain, params, repository: tx.getRepository(SalesProduct), searchables: PRODUCT_SEARCHABLES, field: 'active' })\n const of = (on: boolean) => [...counted].filter(([value]) => (value === 'true' || value === '1') === on).reduce((sum, [, count]) => sum + count, 0)\n return [\n { value: 'active', count: of(true) },\n { value: 'inactive', count: of(false) }\n ]\n}\n"]}
|