u
This commit is contained in:
2
API.md
2
API.md
@@ -34,7 +34,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
|
|||||||
- **google_auth**: `id PK, email:s(320) UQ, picture?:s(2048), picturebinary?:bin, sub?:s(255) UQ, created_at:dt, selected_company?->company`
|
- **google_auth**: `id PK, email:s(320) UQ, picture?:s(2048), picturebinary?:bin, sub?:s(255) UQ, created_at:dt, selected_company?->company`
|
||||||
- **language**: `id PK, iso639:s(10) UQ`
|
- **language**: `id PK, iso639:s(10) UQ`
|
||||||
- **ledger**: `id PK, manifestarticle_id->manifestarticle, transaction_type:s(100), amount:f, transaction_text:txt, hash_version:i, prev_hash?:s(128) [append-only]`
|
- **ledger**: `id PK, manifestarticle_id->manifestarticle, transaction_type:s(100), amount:f, transaction_text:txt, hash_version:i, prev_hash?:s(128) [append-only]`
|
||||||
- **manifest**: `id PK, company_id->company, number:s(100), type:s(100), entity_id?->entity, external_number?:s(100), date:dt`
|
- **manifest**: `id PK, company_id->company, number:s(100), type:s(100), entity_id?->entity, external_number?:s(100), date:dt, created_at:dt`
|
||||||
- **manifestarticle**: `id PK, manifest_id->manifest, sku?:s(100), name?:s(255), pu:f=1, amount:f, net:f, comment?:txt, rebate:f=0, taxrate:f=0`
|
- **manifestarticle**: `id PK, manifest_id->manifest, sku?:s(100), name?:s(255), pu:f=1, amount:f, net:f, comment?:txt, rebate:f=0, taxrate:f=0`
|
||||||
- **manifestattachment**: `id PK, manifest_id->manifest, file_id->file, sort:i=0, UQ(manifest_id, sort)`
|
- **manifestattachment**: `id PK, manifest_id->manifest, file_id->file, sort:i=0, UQ(manifest_id, sort)`
|
||||||
- **meta**: `key:s(255) PK, value:txt`
|
- **meta**: `key:s(255) PK, value:txt`
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
|||||||
|
|
||||||
*Source: `src/store/numbers/list.js`*
|
*Source: `src/store/numbers/list.js`*
|
||||||
|
|
||||||
Lists the number sequences of the user's selected company.
|
Lists the number sequences of the user's selected company. Ensures that default sequences ('sku', 'entity', 'delivery') exist.
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
|
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ const SCHEMA_TABLES = [
|
|||||||
['google_auth', 'id PK, email:s(320) UQ, picture?:s(2048), picturebinary?:bin, sub?:s(255) UQ, created_at:dt, selected_company?->company'],
|
['google_auth', 'id PK, email:s(320) UQ, picture?:s(2048), picturebinary?:bin, sub?:s(255) UQ, created_at:dt, selected_company?->company'],
|
||||||
['language', 'id PK, iso639:s(10) UQ'],
|
['language', 'id PK, iso639:s(10) UQ'],
|
||||||
['ledger', 'id PK, manifestarticle_id->manifestarticle, transaction_type:s(100), amount:f, transaction_text:txt, hash_version:i, prev_hash?:s(128) [append-only]'],
|
['ledger', 'id PK, manifestarticle_id->manifestarticle, transaction_type:s(100), amount:f, transaction_text:txt, hash_version:i, prev_hash?:s(128) [append-only]'],
|
||||||
['manifest', 'id PK, company_id->company, number:s(100), type:s(100), entity_id?->entity, external_number?:s(100), date:dt'],
|
['manifest', 'id PK, company_id->company, number:s(100), type:s(100), entity_id?->entity, external_number?:s(100), date:dt, created_at:dt'],
|
||||||
['manifestarticle', 'id PK, manifest_id->manifest, sku?:s(100), name?:s(255), pu:f=1, amount:f, net:f, comment?:txt, rebate:f=0, taxrate:f=0'],
|
['manifestarticle', 'id PK, manifest_id->manifest, sku?:s(100), name?:s(255), pu:f=1, amount:f, net:f, comment?:txt, rebate:f=0, taxrate:f=0'],
|
||||||
['manifestattachment', 'id PK, manifest_id->manifest, file_id->file, sort:i=0, UQ(manifest_id, sort)'],
|
['manifestattachment', 'id PK, manifest_id->manifest, file_id->file, sort:i=0, UQ(manifest_id, sort)'],
|
||||||
['meta', 'key:s(255) PK, value:txt'],
|
['meta', 'key:s(255) PK, value:txt'],
|
||||||
|
|||||||
@@ -810,7 +810,7 @@ function buildDot(tables, edges, positions = null, anchors = null) {
|
|||||||
// mclimit bumps Graphviz's own crossing-minimization iterations.
|
// mclimit bumps Graphviz's own crossing-minimization iterations.
|
||||||
` graph [rankdir=TB, splines=spline, overlap=false, bgcolor="${COLORS.background}",`,
|
` graph [rankdir=TB, splines=spline, overlap=false, bgcolor="${COLORS.background}",`,
|
||||||
' nodesep=0.95, ranksep=1.0, pad=0.5, fontname="Helvetica",',
|
' nodesep=0.95, ranksep=1.0, pad=0.5, fontname="Helvetica",',
|
||||||
' compound=true, newrank=true, mclimit=4.0];',
|
' compound=true, mclimit=4.0];',
|
||||||
' node [shape=plaintext, fontname="Helvetica"];',
|
' node [shape=plaintext, fontname="Helvetica"];',
|
||||||
' edge [fontname="Helvetica", fontsize=9, penwidth=1.4, arrowsize=0.7];',
|
' edge [fontname="Helvetica", fontsize=9, penwidth=1.4, arrowsize=0.7];',
|
||||||
'',
|
'',
|
||||||
|
|||||||
151
schema-graph.dot
151
schema-graph.dot
File diff suppressed because one or more lines are too long
3253
schema-graph.svg
3253
schema-graph.svg
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 211 KiB |
112
scripts/migrate-manifest-created-at.js
Normal file
112
scripts/migrate-manifest-created-at.js
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import Database from 'better-sqlite3';
|
||||||
|
import { MssqlProvider } from '../src/providers/mssql/index.js';
|
||||||
|
|
||||||
|
async function migrateSqlite(sqlitePath) {
|
||||||
|
console.log(`Migrating SQLite database at: ${sqlitePath}`);
|
||||||
|
const db = new Database(sqlitePath);
|
||||||
|
|
||||||
|
const columns = db.pragma('table_info("manifest")');
|
||||||
|
const hasCreatedAt = columns.some((c) => c.name === 'created_at');
|
||||||
|
if (hasCreatedAt) {
|
||||||
|
console.log('SQLite manifest already has created_at column.');
|
||||||
|
db.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.pragma('foreign_keys = OFF');
|
||||||
|
db.exec(`
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
CREATE TABLE manifest_new (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
company_id INTEGER NOT NULL,
|
||||||
|
"number" TEXT NOT NULL,
|
||||||
|
"type" TEXT NOT NULL,
|
||||||
|
entity_id INTEGER,
|
||||||
|
external_number TEXT,
|
||||||
|
"date" TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
|
FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION,
|
||||||
|
FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
INSERT INTO manifest_new (id, company_id, "number", "type", entity_id, external_number, "date", created_at)
|
||||||
|
SELECT id, company_id, "number", "type", entity_id, external_number, "date", "date"
|
||||||
|
FROM manifest;
|
||||||
|
DROP TABLE manifest;
|
||||||
|
ALTER TABLE manifest_new RENAME TO manifest;
|
||||||
|
COMMIT;
|
||||||
|
`);
|
||||||
|
db.pragma('foreign_keys = ON');
|
||||||
|
|
||||||
|
const fkErrors = db.pragma('foreign_key_check');
|
||||||
|
if (fkErrors.length > 0) {
|
||||||
|
console.error('Foreign key check failed after SQLite migration:', fkErrors);
|
||||||
|
} else {
|
||||||
|
console.log('SQLite migration completed successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Updated SQLite manifest columns:', db.pragma('table_info("manifest")'));
|
||||||
|
console.log('Updated SQLite manifest sample row:', db.prepare('SELECT * FROM manifest LIMIT 1').get());
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migrateMssql() {
|
||||||
|
console.log('Migrating MSSQL database...');
|
||||||
|
const config = {
|
||||||
|
server: process.env.MSSQL_SERVER,
|
||||||
|
database: process.env.MSSQL_DATABASE,
|
||||||
|
user: process.env.MSSQL_USER,
|
||||||
|
password: process.env.MSSQL_PASSWORD,
|
||||||
|
options: {
|
||||||
|
encrypt: false,
|
||||||
|
trustServerCertificate: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const provider = new MssqlProvider(config);
|
||||||
|
try {
|
||||||
|
const pool = await provider.ensurePool();
|
||||||
|
const checkRes = await pool.request().query(`
|
||||||
|
SELECT * FROM sys.columns
|
||||||
|
WHERE object_id = OBJECT_ID(N'manifest') AND name = 'created_at'
|
||||||
|
`);
|
||||||
|
|
||||||
|
if (checkRes.recordset.length > 0) {
|
||||||
|
console.log('MSSQL manifest already has created_at column.');
|
||||||
|
} else {
|
||||||
|
await pool.request().query(`
|
||||||
|
ALTER TABLE manifest ADD created_at DATETIME2 NOT NULL CONSTRAINT DF_manifest_created_at DEFAULT SYSUTCDATETIME();
|
||||||
|
`);
|
||||||
|
await pool.request().query(`
|
||||||
|
UPDATE manifest SET created_at = [date];
|
||||||
|
`);
|
||||||
|
console.log('MSSQL migration completed successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const colsRes = await pool.request().query(`
|
||||||
|
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_NAME = 'manifest'
|
||||||
|
`);
|
||||||
|
console.log('Updated MSSQL manifest columns:', colsRes.recordset);
|
||||||
|
const sampleRes = await pool.request().query(`
|
||||||
|
SELECT TOP 1 * FROM manifest
|
||||||
|
`);
|
||||||
|
console.log('Updated MSSQL manifest sample row:', sampleRes.recordset[0]);
|
||||||
|
await provider.close();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('MSSQL migration failed:', err);
|
||||||
|
await provider.close();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
await migrateSqlite('/home/seb/src/dashback/data/dash.db');
|
||||||
|
await migrateMssql();
|
||||||
|
}
|
||||||
|
|
||||||
|
run().catch((err) => {
|
||||||
|
console.error('Migration failed:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -55,6 +55,7 @@ async function run() {
|
|||||||
|
|
||||||
// Create a delivery manifest with lines
|
// Create a delivery manifest with lines
|
||||||
const delivery = await store.delivery.create(googleAuthId, {
|
const delivery = await store.delivery.create(googleAuthId, {
|
||||||
|
supplierId: supplier.id,
|
||||||
externalNumber: 'DEL-1',
|
externalNumber: 'DEL-1',
|
||||||
date: new Date().toISOString(),
|
date: new Date().toISOString(),
|
||||||
lines: [
|
lines: [
|
||||||
@@ -217,9 +218,15 @@ async function run() {
|
|||||||
const history = await store.article.getHistoryBySku(googleAuthId, article.sku);
|
const history = await store.article.getHistoryBySku(googleAuthId, article.sku);
|
||||||
const historyTexts = history.map((h) => h.text);
|
const historyTexts = history.map((h) => h.text);
|
||||||
console.log('Article history texts:', historyTexts);
|
console.log('Article history texts:', historyTexts);
|
||||||
assert(historyTexts.includes('Initial delivery movement'), 'History should include initial delivery movement');
|
assert(
|
||||||
assert(historyTexts.includes('Damaged item written off'), 'History should include damaged item written off');
|
historyTexts.some((t) => t.includes('Initial delivery movement') && t.includes('Lieferant: "Supplier X"') && t.includes('EK brutto: 11.90') && t.includes('Menge: 5')),
|
||||||
assert(historyTexts.includes('Second line goods receipt'), 'History should include second line goods receipt');
|
'History should include initial delivery movement with supplier, EK brutto, and amount',
|
||||||
|
);
|
||||||
|
assert(historyTexts.includes('Damaged item written off'), 'History should include undamaged adjustment memo as is');
|
||||||
|
assert(
|
||||||
|
historyTexts.some((t) => t.includes('Second line goods receipt') && t.includes('Lieferant: "Supplier X"') && t.includes('EK brutto: 23.80') && t.includes('Menge: 10')),
|
||||||
|
'History should include second line goods receipt with details',
|
||||||
|
);
|
||||||
|
|
||||||
console.log('✓ All ledger tests passed successfully!');
|
console.log('✓ All ledger tests passed successfully!');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
147
scripts/test-manifest-two-dates.js
Normal file
147
scripts/test-manifest-two-dates.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import 'dotenv/config';
|
||||||
|
import LibDashStore from '../src/index.js';
|
||||||
|
import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js';
|
||||||
|
import { MssqlProvider } from '../src/providers/mssql/index.js';
|
||||||
|
|
||||||
|
async function testProvider(name, provider) {
|
||||||
|
console.log(`\n=== Testing ${name} ===`);
|
||||||
|
await provider.initialize();
|
||||||
|
const store = await new LibDashStore(provider);
|
||||||
|
|
||||||
|
// 1. Setup company and user
|
||||||
|
const email = `test-manifest-${Date.now()}@example.com`;
|
||||||
|
const googleAuthId = await store.google_auth.add(email);
|
||||||
|
const company = await store.company.create({
|
||||||
|
name: `Test Manifest Corp ${Date.now()}`,
|
||||||
|
iso639: 'de',
|
||||||
|
taxrate: 19,
|
||||||
|
reducedtaxrate: 7,
|
||||||
|
googleAuthId,
|
||||||
|
});
|
||||||
|
await store.google_auth.setSelectedCompany(googleAuthId, company.id);
|
||||||
|
|
||||||
|
// 2. Test delivery with both dates
|
||||||
|
console.log('Testing delivery creation with date and createdAt...');
|
||||||
|
const deliveryDate = '2026-10-15T12:00:00.000Z';
|
||||||
|
const customCreatedAt = '2026-09-01T08:30:00.000Z';
|
||||||
|
|
||||||
|
const createdDelivery = await store.delivery.create(googleAuthId, {
|
||||||
|
externalNumber: 'DEL-2DATES-1',
|
||||||
|
date: deliveryDate,
|
||||||
|
createdAt: customCreatedAt,
|
||||||
|
lines: [
|
||||||
|
{ name: 'Article 1', amount: 2, net: 50, taxrate: 19 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(createdDelivery.id, 'Delivery should have an id');
|
||||||
|
assert(createdDelivery.date, 'Delivery should have a delivery date');
|
||||||
|
assert(createdDelivery.createdAt, 'Delivery should have createdAt');
|
||||||
|
console.log('Delivery created:', {
|
||||||
|
id: createdDelivery.id,
|
||||||
|
date: createdDelivery.date,
|
||||||
|
createdAt: createdDelivery.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify delivery getById
|
||||||
|
const fetchedDelivery = await store.delivery.getById(googleAuthId, createdDelivery.id);
|
||||||
|
assert.equal(new Date(fetchedDelivery.date).toISOString().slice(0, 10), '2026-10-15');
|
||||||
|
assert.equal(new Date(fetchedDelivery.createdAt).toISOString().slice(0, 10), '2026-09-01');
|
||||||
|
|
||||||
|
// Verify delivery update
|
||||||
|
const updatedDeliveryDate = '2026-11-20T12:00:00.000Z';
|
||||||
|
const updatedDelivery = await store.delivery.update(googleAuthId, createdDelivery.id, {
|
||||||
|
date: updatedDeliveryDate,
|
||||||
|
});
|
||||||
|
assert.equal(new Date(updatedDelivery.date).toISOString().slice(0, 10), '2026-11-20');
|
||||||
|
assert.equal(new Date(updatedDelivery.createdAt).toISOString().slice(0, 10), '2026-09-01');
|
||||||
|
|
||||||
|
// Verify delivery.list ledger info before booking
|
||||||
|
let listBefore = await store.delivery.list(googleAuthId);
|
||||||
|
const foundBefore = listBefore.find((d) => d.id === createdDelivery.id);
|
||||||
|
assert(foundBefore, 'Delivery should be in delivery.list');
|
||||||
|
assert.equal(foundBefore.ledger.totalPositions, 1, 'Delivery ledger should have 1 totalPosition');
|
||||||
|
assert.equal(foundBefore.ledger.completelyOnLedgerPositions, 0, 'Delivery ledger should have 0 completelyOnLedgerPositions initially');
|
||||||
|
assert.equal(foundBefore.totalPositions, undefined, 'totalPositions should not be at top level');
|
||||||
|
assert.equal(foundBefore.completelyOnLedgerPositions, undefined, 'completelyOnLedgerPositions should not be at top level');
|
||||||
|
|
||||||
|
// Book line to ledger
|
||||||
|
await store.ledger.add(googleAuthId, {
|
||||||
|
manifestarticle_id: createdDelivery.lines[0].id,
|
||||||
|
transaction_type: 'DELIVERY',
|
||||||
|
amount: 2,
|
||||||
|
transaction_text: `Lieferung ${createdDelivery.id}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify delivery.list ledger info after booking
|
||||||
|
let listAfter = await store.delivery.list(googleAuthId);
|
||||||
|
const foundAfter = listAfter.find((d) => d.id === createdDelivery.id);
|
||||||
|
assert(foundAfter, 'Delivery should be in delivery.list after booking');
|
||||||
|
assert.equal(foundAfter.ledger.totalPositions, 1, 'Delivery ledger should have 1 totalPosition');
|
||||||
|
assert.equal(foundAfter.ledger.completelyOnLedgerPositions, 1, 'Delivery ledger should have 1 completelyOnLedgerPosition after booking');
|
||||||
|
console.log('✓ Verified delivery.list ledger info (unbooked -> booked):', foundAfter.ledger);
|
||||||
|
|
||||||
|
// 3. Test quote with both dates
|
||||||
|
console.log('Testing quote creation...');
|
||||||
|
const quote = await store.quote.create(googleAuthId, {
|
||||||
|
number: `Q-${Date.now()}`,
|
||||||
|
date: '2026-12-01T00:00:00.000Z',
|
||||||
|
createdAt: '2026-09-10T00:00:00.000Z',
|
||||||
|
lines: [{ name: 'Quote item', amount: 1, net: 100, taxrate: 19 }],
|
||||||
|
});
|
||||||
|
assert(quote.createdAt, 'Quote should have createdAt');
|
||||||
|
assert(quote.date, 'Quote should have date');
|
||||||
|
|
||||||
|
// 4. Test order with default createdAt
|
||||||
|
console.log('Testing order creation with default createdAt...');
|
||||||
|
const order = await store.order.create(googleAuthId, {
|
||||||
|
number: `ORD-${Date.now()}`,
|
||||||
|
date: '2026-12-15T00:00:00.000Z',
|
||||||
|
lines: [{ name: 'Order item', amount: 3, net: 25, taxrate: 19 }],
|
||||||
|
});
|
||||||
|
assert(order.createdAt, 'Order should have default createdAt populated');
|
||||||
|
assert(order.date, 'Order should have date');
|
||||||
|
console.log('Order created with auto createdAt:', {
|
||||||
|
id: order.id,
|
||||||
|
date: order.date,
|
||||||
|
createdAt: order.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✓ All tests passed for ${name}!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// Test SQLite provider with existing dashback database
|
||||||
|
const sqliteProvider = new BetterSqlite3Provider({
|
||||||
|
filename: '/home/seb/src/dashback/data/dash.db',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await testProvider('SQLite (dash.db)', sqliteProvider);
|
||||||
|
} finally {
|
||||||
|
sqliteProvider.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test MSSQL provider
|
||||||
|
const mssqlConfig = {
|
||||||
|
server: process.env.MSSQL_SERVER,
|
||||||
|
database: process.env.MSSQL_DATABASE,
|
||||||
|
user: process.env.MSSQL_USER,
|
||||||
|
password: process.env.MSSQL_PASSWORD,
|
||||||
|
options: {
|
||||||
|
encrypt: false,
|
||||||
|
trustServerCertificate: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mssqlProvider = new MssqlProvider(mssqlConfig);
|
||||||
|
try {
|
||||||
|
await testProvider('MSSQL (dashback)', mssqlProvider);
|
||||||
|
} finally {
|
||||||
|
await mssqlProvider.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error('Test failed:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -7,10 +7,30 @@ SELECT
|
|||||||
m.[type],
|
m.[type],
|
||||||
m.entity_id AS supplierId,
|
m.entity_id AS supplierId,
|
||||||
m.external_number AS externalNumber,
|
m.external_number AS externalNumber,
|
||||||
m.[date]
|
m.[date],
|
||||||
|
m.created_at AS createdAt,
|
||||||
|
COUNT(ma.id) AS totalPositions,
|
||||||
|
COUNT(CASE WHEN ma.id IS NOT NULL AND (
|
||||||
|
(ma.amount > 0 AND COALESCE(ma_ledger.booked_amount, 0) >= ma.amount) OR
|
||||||
|
(ma.amount <= 0 AND COALESCE(ma_ledger.booked_amount, 0) > 0)
|
||||||
|
) THEN 1 END) AS completelyOnLedgerPositions
|
||||||
FROM manifest m
|
FROM manifest m
|
||||||
|
LEFT JOIN manifestarticle ma ON ma.manifest_id = m.id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT manifestarticle_id, SUM(amount) AS booked_amount
|
||||||
|
FROM ledger
|
||||||
|
GROUP BY manifestarticle_id
|
||||||
|
) ma_ledger ON ma_ledger.manifestarticle_id = ma.id
|
||||||
WHERE m.company_id = @companyId
|
WHERE m.company_id = @companyId
|
||||||
AND m.[type] = @type
|
AND m.[type] = @type
|
||||||
|
GROUP BY
|
||||||
|
m.id,
|
||||||
|
m.[number],
|
||||||
|
m.[type],
|
||||||
|
m.entity_id,
|
||||||
|
m.external_number,
|
||||||
|
m.[date],
|
||||||
|
m.created_at
|
||||||
ORDER BY m.[date] DESC, m.id DESC
|
ORDER BY m.[date] DESC, m.id DESC
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -21,11 +41,31 @@ SELECT
|
|||||||
m.[type],
|
m.[type],
|
||||||
m.entity_id AS supplierId,
|
m.entity_id AS supplierId,
|
||||||
m.external_number AS externalNumber,
|
m.external_number AS externalNumber,
|
||||||
m.[date]
|
m.[date],
|
||||||
|
m.created_at AS createdAt,
|
||||||
|
COUNT(ma.id) AS totalPositions,
|
||||||
|
COUNT(CASE WHEN ma.id IS NOT NULL AND (
|
||||||
|
(ma.amount > 0 AND COALESCE(ma_ledger.booked_amount, 0) >= ma.amount) OR
|
||||||
|
(ma.amount <= 0 AND COALESCE(ma_ledger.booked_amount, 0) > 0)
|
||||||
|
) THEN 1 END) AS completelyOnLedgerPositions
|
||||||
FROM manifest m
|
FROM manifest m
|
||||||
|
LEFT JOIN manifestarticle ma ON ma.manifest_id = m.id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT manifestarticle_id, SUM(amount) AS booked_amount
|
||||||
|
FROM ledger
|
||||||
|
GROUP BY manifestarticle_id
|
||||||
|
) ma_ledger ON ma_ledger.manifestarticle_id = ma.id
|
||||||
WHERE m.id = @id
|
WHERE m.id = @id
|
||||||
AND m.company_id = @companyId
|
AND m.company_id = @companyId
|
||||||
AND m.[type] = @type
|
AND m.[type] = @type
|
||||||
|
GROUP BY
|
||||||
|
m.id,
|
||||||
|
m.[number],
|
||||||
|
m.[type],
|
||||||
|
m.entity_id,
|
||||||
|
m.external_number,
|
||||||
|
m.[date],
|
||||||
|
m.created_at
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_DELIVERY_LINES = `
|
export const GET_DELIVERY_LINES = `
|
||||||
@@ -45,9 +85,9 @@ ORDER BY ma.id
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const CREATE_MANIFEST = `
|
export const CREATE_MANIFEST = `
|
||||||
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date])
|
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date], created_at)
|
||||||
OUTPUT INSERTED.id
|
OUTPUT INSERTED.id
|
||||||
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date)
|
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date, COALESCE(@createdAt, SYSUTCDATETIME()))
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const UPDATE_MANIFEST = `
|
export const UPDATE_MANIFEST = `
|
||||||
@@ -56,7 +96,8 @@ SET
|
|||||||
[number] = @number,
|
[number] = @number,
|
||||||
entity_id = @entityId,
|
entity_id = @entityId,
|
||||||
external_number = @externalNumber,
|
external_number = @externalNumber,
|
||||||
[date] = @date
|
[date] = @date,
|
||||||
|
created_at = COALESCE(@createdAt, created_at)
|
||||||
WHERE id = @id
|
WHERE id = @id
|
||||||
AND company_id = @companyId
|
AND company_id = @companyId
|
||||||
AND [type] = @type
|
AND [type] = @type
|
||||||
|
|||||||
@@ -1,7 +1,19 @@
|
|||||||
export const CHECK_MANIFESTARTICLE_ACCESS = `
|
export const CHECK_MANIFESTARTICLE_ACCESS = `
|
||||||
SELECT ma.id, ma.sku
|
SELECT
|
||||||
|
ma.id,
|
||||||
|
ma.sku,
|
||||||
|
ma.amount AS lineAmount,
|
||||||
|
ma.net,
|
||||||
|
ma.rebate,
|
||||||
|
ma.taxrate,
|
||||||
|
m.id AS manifestId,
|
||||||
|
m.[number] AS manifestNumber,
|
||||||
|
m.[type] AS manifestType,
|
||||||
|
m.external_number AS externalNumber,
|
||||||
|
e.name AS supplierName
|
||||||
FROM manifestarticle ma
|
FROM manifestarticle ma
|
||||||
INNER JOIN manifest m ON ma.manifest_id = m.id
|
INNER JOIN manifest m ON ma.manifest_id = m.id
|
||||||
|
LEFT JOIN entity e ON e.id = m.entity_id
|
||||||
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
|
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ SELECT
|
|||||||
m.[type],
|
m.[type],
|
||||||
m.entity_id AS entityId,
|
m.entity_id AS entityId,
|
||||||
m.external_number AS externalNumber,
|
m.external_number AS externalNumber,
|
||||||
m.[date]
|
m.[date],
|
||||||
|
m.created_at AS createdAt
|
||||||
FROM manifest m
|
FROM manifest m
|
||||||
WHERE m.company_id = @companyId
|
WHERE m.company_id = @companyId
|
||||||
AND m.[type] = @type
|
AND m.[type] = @type
|
||||||
@@ -27,7 +28,8 @@ SELECT
|
|||||||
m.[type],
|
m.[type],
|
||||||
m.entity_id AS entityId,
|
m.entity_id AS entityId,
|
||||||
m.external_number AS externalNumber,
|
m.external_number AS externalNumber,
|
||||||
m.[date]
|
m.[date],
|
||||||
|
m.created_at AS createdAt
|
||||||
FROM manifest m
|
FROM manifest m
|
||||||
WHERE m.id = @id
|
WHERE m.id = @id
|
||||||
AND m.company_id = @companyId
|
AND m.company_id = @companyId
|
||||||
@@ -51,9 +53,9 @@ ORDER BY ma.id
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const CREATE_MANIFEST = `
|
export const CREATE_MANIFEST = `
|
||||||
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date])
|
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date], created_at)
|
||||||
OUTPUT INSERTED.id
|
OUTPUT INSERTED.id
|
||||||
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date)
|
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date, COALESCE(@createdAt, SYSUTCDATETIME()))
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const UPDATE_MANIFEST = `
|
export const UPDATE_MANIFEST = `
|
||||||
@@ -62,7 +64,8 @@ SET
|
|||||||
[number] = @number,
|
[number] = @number,
|
||||||
entity_id = @entityId,
|
entity_id = @entityId,
|
||||||
external_number = @externalNumber,
|
external_number = @externalNumber,
|
||||||
[date] = @date
|
[date] = @date,
|
||||||
|
created_at = COALESCE(@createdAt, created_at)
|
||||||
WHERE id = @id
|
WHERE id = @id
|
||||||
AND company_id = @companyId
|
AND company_id = @companyId
|
||||||
AND [type] = @type
|
AND [type] = @type
|
||||||
|
|||||||
@@ -565,6 +565,7 @@ BEGIN
|
|||||||
entity_id INT NULL,
|
entity_id INT NULL,
|
||||||
external_number NVARCHAR(100) NULL,
|
external_number NVARCHAR(100) NULL,
|
||||||
[date] DATETIME2 NOT NULL CONSTRAINT DF_manifest_date DEFAULT SYSUTCDATETIME(),
|
[date] DATETIME2 NOT NULL CONSTRAINT DF_manifest_date DEFAULT SYSUTCDATETIME(),
|
||||||
|
created_at DATETIME2 NOT NULL CONSTRAINT DF_manifest_created_at DEFAULT SYSUTCDATETIME(),
|
||||||
CONSTRAINT FK_manifest_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION,
|
CONSTRAINT FK_manifest_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION,
|
||||||
CONSTRAINT FK_manifest_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL
|
CONSTRAINT FK_manifest_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,19 @@
|
|||||||
export const CHECK_MANIFESTARTICLE_ACCESS = `
|
export const CHECK_MANIFESTARTICLE_ACCESS = `
|
||||||
SELECT ma.id, ma.sku
|
SELECT
|
||||||
|
ma.id,
|
||||||
|
ma.sku,
|
||||||
|
ma.amount AS lineAmount,
|
||||||
|
ma.net,
|
||||||
|
ma.rebate,
|
||||||
|
ma.taxrate,
|
||||||
|
m.id AS manifestId,
|
||||||
|
m.number AS manifestNumber,
|
||||||
|
m.type AS manifestType,
|
||||||
|
m.external_number AS externalNumber,
|
||||||
|
e.name AS supplierName
|
||||||
FROM manifestarticle ma
|
FROM manifestarticle ma
|
||||||
INNER JOIN manifest m ON ma.manifest_id = m.id
|
INNER JOIN manifest m ON ma.manifest_id = m.id
|
||||||
|
LEFT JOIN entity e ON e.id = m.entity_id
|
||||||
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
|
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -404,6 +404,7 @@ CREATE TABLE IF NOT EXISTS manifest (
|
|||||||
entity_id INTEGER,
|
entity_id INTEGER,
|
||||||
external_number TEXT,
|
external_number TEXT,
|
||||||
"date" TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
"date" TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
||||||
|
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
||||||
FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION,
|
FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION,
|
||||||
FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL
|
FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ export default function create(provider) {
|
|||||||
if (!date) {
|
if (!date) {
|
||||||
throw new Error('Invalid delivery');
|
throw new Error('Invalid delivery');
|
||||||
}
|
}
|
||||||
|
const createdAt = body.createdAt ? parseDate(body.createdAt) : new Date();
|
||||||
|
if (body.createdAt && !createdAt) {
|
||||||
|
throw new Error('Invalid delivery');
|
||||||
|
}
|
||||||
const externalNumber = body.externalNumber?.trim?.()
|
const externalNumber = body.externalNumber?.trim?.()
|
||||||
? body.externalNumber.trim()
|
? body.externalNumber.trim()
|
||||||
: null;
|
: null;
|
||||||
@@ -46,6 +50,7 @@ export default function create(provider) {
|
|||||||
entityId: supplierId,
|
entityId: supplierId,
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date,
|
date,
|
||||||
|
createdAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
const manifestId = created.rows[0]?.id ?? created.insertId;
|
const manifestId = created.rows[0]?.id ?? created.insertId;
|
||||||
|
|||||||
@@ -7,14 +7,24 @@ import { loadAttachments } from './attachments.js';
|
|||||||
export const DELIVERY_TYPE = mssqlQueries.DELIVERY_TYPE;
|
export const DELIVERY_TYPE = mssqlQueries.DELIVERY_TYPE;
|
||||||
|
|
||||||
export function shapeSummary(row) {
|
export function shapeSummary(row) {
|
||||||
return {
|
const summary = {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
number: row.number,
|
number: row.number,
|
||||||
type: DELIVERY_TYPE,
|
type: DELIVERY_TYPE,
|
||||||
supplierId: row.supplierId ?? null,
|
supplierId: row.supplierId ?? null,
|
||||||
externalNumber: row.externalNumber ?? null,
|
externalNumber: row.externalNumber ?? null,
|
||||||
date: row.date,
|
date: row.date,
|
||||||
|
createdAt: row.createdAt ?? row.created_at ?? null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (row.totalPositions !== undefined || row.completelyOnLedgerPositions !== undefined) {
|
||||||
|
summary.ledger = {
|
||||||
|
totalPositions: Number(row.totalPositions ?? 0),
|
||||||
|
completelyOnLedgerPositions: Number(row.completelyOnLedgerPositions ?? 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadDelivery(provider, tx, companyId, id) {
|
export async function loadDelivery(provider, tx, companyId, id) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export default function update(provider) {
|
|||||||
|
|
||||||
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
||||||
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
|
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
|
||||||
|
const hasCreatedAt = Object.prototype.hasOwnProperty.call(patch, 'createdAt');
|
||||||
const hasSupplierId = Object.prototype.hasOwnProperty.call(
|
const hasSupplierId = Object.prototype.hasOwnProperty.call(
|
||||||
patch,
|
patch,
|
||||||
'supplierId',
|
'supplierId',
|
||||||
@@ -29,7 +30,7 @@ export default function update(provider) {
|
|||||||
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
||||||
const hasFiles = Object.prototype.hasOwnProperty.call(patch, 'files');
|
const hasFiles = Object.prototype.hasOwnProperty.call(patch, 'files');
|
||||||
|
|
||||||
if (!hasNumber && !hasDate && !hasSupplierId && !hasExternalNumber && !hasLines && !hasFiles) {
|
if (!hasNumber && !hasDate && !hasCreatedAt && !hasSupplierId && !hasExternalNumber && !hasLines && !hasFiles) {
|
||||||
throw new Error('Invalid delivery');
|
throw new Error('Invalid delivery');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +71,14 @@ export default function update(provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let createdAt = summary.createdAt ? parseDate(summary.createdAt) : null;
|
||||||
|
if (hasCreatedAt) {
|
||||||
|
createdAt = parseDate(patch.createdAt);
|
||||||
|
if (!createdAt) {
|
||||||
|
throw new Error('Invalid delivery');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let supplierId = summary.supplierId;
|
let supplierId = summary.supplierId;
|
||||||
if (hasSupplierId) {
|
if (hasSupplierId) {
|
||||||
supplierId = await assertSupplier(
|
supplierId = await assertSupplier(
|
||||||
@@ -95,6 +104,7 @@ export default function update(provider) {
|
|||||||
entityId: supplierId,
|
entityId: supplierId,
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date,
|
date,
|
||||||
|
createdAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasLines) {
|
if (hasLines) {
|
||||||
|
|||||||
@@ -142,11 +142,45 @@ export default function add(provider) {
|
|||||||
companyId,
|
companyId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const upperType = String(transactionType).toUpperCase();
|
||||||
|
const isDelivery =
|
||||||
|
upperType === 'DELIVERY' ||
|
||||||
|
upperType === 'GOODS_RECEIPT' ||
|
||||||
|
(manifestArticle.manifestType === 'delivery' &&
|
||||||
|
!upperType.includes('ADJUST') &&
|
||||||
|
!upperType.includes('LOSS') &&
|
||||||
|
!upperType.includes('WRITE_OFF'));
|
||||||
|
|
||||||
|
let historyText = transactionText;
|
||||||
|
if (isDelivery) {
|
||||||
|
const details = [];
|
||||||
|
if (manifestArticle.supplierName) {
|
||||||
|
details.push(`Lieferant: "${manifestArticle.supplierName}"`);
|
||||||
|
}
|
||||||
|
if (manifestArticle.net != null && Number.isFinite(Number(manifestArticle.net))) {
|
||||||
|
const net = Number(manifestArticle.net);
|
||||||
|
const rebate = Number(manifestArticle.rebate ?? 0);
|
||||||
|
const taxrate = Number(manifestArticle.taxrate ?? 0);
|
||||||
|
const netAfterRebate = net * (1 - rebate / 100);
|
||||||
|
const brutto = netAfterRebate * (1 + taxrate / 100);
|
||||||
|
const ekBrutto = (Math.round(brutto * 100) / 100).toFixed(2);
|
||||||
|
details.push(`EK brutto: ${ekBrutto}`);
|
||||||
|
}
|
||||||
|
if (amount != null && Number.isFinite(Number(amount))) {
|
||||||
|
details.push(`Menge: ${amount}`);
|
||||||
|
}
|
||||||
|
const suffix = details.length ? ` (${details.join(', ')})` : '';
|
||||||
|
const base =
|
||||||
|
transactionText?.trim() ||
|
||||||
|
(manifestArticle.manifestNumber ? `Lieferung ${manifestArticle.manifestNumber}` : 'Lieferung');
|
||||||
|
historyText = `${base}${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
for (const match of articleMatches.rows) {
|
for (const match of articleMatches.rows) {
|
||||||
await runner.query(queries.INSERT_ARTICLE_HISTORY, {
|
await runner.query(queries.INSERT_ARTICLE_HISTORY, {
|
||||||
articleId: match.articleId,
|
articleId: match.articleId,
|
||||||
companyuserId,
|
companyuserId,
|
||||||
text: transactionText,
|
text: historyText,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export function shapeSummary(type, row) {
|
|||||||
entityId: row.entityId ?? null,
|
entityId: row.entityId ?? null,
|
||||||
externalNumber: row.externalNumber ?? null,
|
externalNumber: row.externalNumber ?? null,
|
||||||
date: row.date,
|
date: row.date,
|
||||||
|
createdAt: row.createdAt ?? row.created_at ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ export default function create(provider) {
|
|||||||
if (!number || !date) {
|
if (!number || !date) {
|
||||||
throw new Error('Invalid order');
|
throw new Error('Invalid order');
|
||||||
}
|
}
|
||||||
|
const createdAt = body.createdAt ? parseDate(body.createdAt) : new Date();
|
||||||
|
if (body.createdAt && !createdAt) {
|
||||||
|
throw new Error('Invalid order');
|
||||||
|
}
|
||||||
const externalNumber = body.externalNumber?.trim?.()
|
const externalNumber = body.externalNumber?.trim?.()
|
||||||
? body.externalNumber.trim()
|
? body.externalNumber.trim()
|
||||||
: null;
|
: null;
|
||||||
@@ -66,6 +70,7 @@ export default function create(provider) {
|
|||||||
entityId,
|
entityId,
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date,
|
date,
|
||||||
|
createdAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
const manifestId = created.rows[0]?.id ?? created.insertId;
|
const manifestId = created.rows[0]?.id ?? created.insertId;
|
||||||
|
|||||||
@@ -52,11 +52,12 @@ export default function update(provider) {
|
|||||||
|
|
||||||
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
||||||
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
|
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
|
||||||
|
const hasCreatedAt = Object.prototype.hasOwnProperty.call(patch, 'createdAt');
|
||||||
const hasEntityId = Object.prototype.hasOwnProperty.call(patch, 'entityId');
|
const hasEntityId = Object.prototype.hasOwnProperty.call(patch, 'entityId');
|
||||||
const hasExternalNumber = Object.prototype.hasOwnProperty.call(patch, 'externalNumber');
|
const hasExternalNumber = Object.prototype.hasOwnProperty.call(patch, 'externalNumber');
|
||||||
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
||||||
|
|
||||||
if (!hasNumber && !hasDate && !hasEntityId && !hasExternalNumber && !hasLines) {
|
if (!hasNumber && !hasDate && !hasCreatedAt && !hasEntityId && !hasExternalNumber && !hasLines) {
|
||||||
throw new Error('Invalid order');
|
throw new Error('Invalid order');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +98,14 @@ export default function update(provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let createdAt = summary.createdAt ? parseDate(summary.createdAt) : null;
|
||||||
|
if (hasCreatedAt) {
|
||||||
|
createdAt = parseDate(patch.createdAt);
|
||||||
|
if (!createdAt) {
|
||||||
|
throw new Error('Invalid order');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let entityId = summary.entityId;
|
let entityId = summary.entityId;
|
||||||
if (hasEntityId) {
|
if (hasEntityId) {
|
||||||
entityId = await assertCustomerEntity(
|
entityId = await assertCustomerEntity(
|
||||||
@@ -121,6 +130,7 @@ export default function update(provider) {
|
|||||||
entityId,
|
entityId,
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date,
|
date,
|
||||||
|
createdAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasLines) {
|
if (hasLines) {
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ export default function create(provider) {
|
|||||||
if (!number || !date) {
|
if (!number || !date) {
|
||||||
throw new Error('Invalid quote');
|
throw new Error('Invalid quote');
|
||||||
}
|
}
|
||||||
|
const createdAt = body.createdAt ? parseDate(body.createdAt) : new Date();
|
||||||
|
if (body.createdAt && !createdAt) {
|
||||||
|
throw new Error('Invalid quote');
|
||||||
|
}
|
||||||
const externalNumber = body.externalNumber?.trim?.()
|
const externalNumber = body.externalNumber?.trim?.()
|
||||||
? body.externalNumber.trim()
|
? body.externalNumber.trim()
|
||||||
: null;
|
: null;
|
||||||
@@ -66,6 +70,7 @@ export default function create(provider) {
|
|||||||
entityId,
|
entityId,
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date,
|
date,
|
||||||
|
createdAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
const manifestId = created.rows[0]?.id ?? created.insertId;
|
const manifestId = created.rows[0]?.id ?? created.insertId;
|
||||||
|
|||||||
@@ -52,11 +52,12 @@ export default function update(provider) {
|
|||||||
|
|
||||||
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
|
||||||
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
|
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
|
||||||
|
const hasCreatedAt = Object.prototype.hasOwnProperty.call(patch, 'createdAt');
|
||||||
const hasEntityId = Object.prototype.hasOwnProperty.call(patch, 'entityId');
|
const hasEntityId = Object.prototype.hasOwnProperty.call(patch, 'entityId');
|
||||||
const hasExternalNumber = Object.prototype.hasOwnProperty.call(patch, 'externalNumber');
|
const hasExternalNumber = Object.prototype.hasOwnProperty.call(patch, 'externalNumber');
|
||||||
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
|
||||||
|
|
||||||
if (!hasNumber && !hasDate && !hasEntityId && !hasExternalNumber && !hasLines) {
|
if (!hasNumber && !hasDate && !hasCreatedAt && !hasEntityId && !hasExternalNumber && !hasLines) {
|
||||||
throw new Error('Invalid quote');
|
throw new Error('Invalid quote');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +98,14 @@ export default function update(provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let createdAt = summary.createdAt ? parseDate(summary.createdAt) : null;
|
||||||
|
if (hasCreatedAt) {
|
||||||
|
createdAt = parseDate(patch.createdAt);
|
||||||
|
if (!createdAt) {
|
||||||
|
throw new Error('Invalid quote');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let entityId = summary.entityId;
|
let entityId = summary.entityId;
|
||||||
if (hasEntityId) {
|
if (hasEntityId) {
|
||||||
entityId = await assertCustomerEntity(
|
entityId = await assertCustomerEntity(
|
||||||
@@ -121,6 +130,7 @@ export default function update(provider) {
|
|||||||
entityId,
|
entityId,
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date,
|
date,
|
||||||
|
createdAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasLines) {
|
if (hasLines) {
|
||||||
|
|||||||
Reference in New Issue
Block a user