gemcap-be-common 1.4.152 → 1.4.154

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.
@@ -41,7 +41,6 @@ export interface IFinancialSpreadingView {
41
41
  month: number;
42
42
  sheetId: string;
43
43
  amount: number;
44
- isFixedCost?: boolean;
45
44
  minus_1?: number;
46
45
  minus_2?: number;
47
46
  minus_3?: number;
@@ -24,7 +24,6 @@ exports.financialSpreadingViewValidationSchema = joi_1.default.object({
24
24
  year: joi_1.default.number().integer().min(1900).max(9999),
25
25
  sheetId: joi_1.default.string().required(),
26
26
  amount: joi_1.default.number().required().allow(0),
27
- isFixedCost: joi_1.default.boolean().allow(null),
28
27
  minus_1: joi_1.default.number(),
29
28
  minus_2: joi_1.default.number(),
30
29
  minus_3: joi_1.default.number(),
@@ -21,7 +21,6 @@ export interface IFinancialSpreadingView {
21
21
  month: number;
22
22
  sheetId: string;
23
23
  amount: number;
24
- isFixedCost?: boolean;
25
24
  minus_1?: number;
26
25
  minus_2?: number;
27
26
  minus_3?: number;
@@ -81,7 +80,6 @@ export const financialSpreadingViewValidationSchema = Joi.object<IFinancialSprea
81
80
  year: Joi.number().integer().min(1900).max(9999),
82
81
  sheetId: Joi.string().required(),
83
82
  amount: Joi.number().required().allow(0),
84
- isFixedCost: Joi.boolean().allow(null),
85
83
  minus_1: Joi.number(),
86
84
  minus_2: Joi.number(),
87
85
  minus_3: Joi.number(),
@@ -31,10 +31,10 @@ export declare const allSchemas: {
31
31
  createdAt: NativeDate;
32
32
  updatedAt: NativeDate;
33
33
  } & {
34
- order: number;
34
+ amount: number;
35
35
  bbcSheetId: import("mongoose").Types.ObjectId;
36
+ order: number;
36
37
  apDate: Date;
37
- amount: number;
38
38
  __v?: number;
39
39
  poNumber?: string;
40
40
  customerName?: string;
@@ -45,10 +45,10 @@ export declare const allSchemas: {
45
45
  createdAt: NativeDate;
46
46
  updatedAt: NativeDate;
47
47
  } & {
48
- order: number;
48
+ amount: number;
49
49
  bbcSheetId: import("mongoose").Types.ObjectId;
50
+ order: number;
50
51
  apDate: Date;
51
- amount: number;
52
52
  __v?: number;
53
53
  poNumber?: string;
54
54
  customerName?: string;
@@ -59,10 +59,10 @@ export declare const allSchemas: {
59
59
  createdAt: NativeDate;
60
60
  updatedAt: NativeDate;
61
61
  } & {
62
- order: number;
62
+ amount: number;
63
63
  bbcSheetId: import("mongoose").Types.ObjectId;
64
+ order: number;
64
65
  apDate: Date;
65
- amount: number;
66
66
  __v?: number;
67
67
  poNumber?: string;
68
68
  customerName?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemcap-be-common",
3
- "version": "1.4.152",
3
+ "version": "1.4.154",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -196,11 +196,10 @@ class FinancialSpreadingService {
196
196
  return { success: false, data, sheets, message: 'Invalid data' };
197
197
  }
198
198
  await Promise.all(editableData.map(async (item) => {
199
- const { _id, isFixedCost, ...rest } = item;
199
+ const { _id, ...rest } = item;
200
200
  const update = {
201
201
  amount: rest.amount,
202
202
  };
203
- console.debug({ _id, update });
204
203
  if (_id.includes('new_')) {
205
204
  if (rest.sheetId.includes('new_')) {
206
205
  rest.sheetId = sheetMap[rest.sheetId];
@@ -403,23 +402,62 @@ class FinancialSpreadingService {
403
402
  v: header,
404
403
  s: { font: { bold: true } },
405
404
  }));
405
+ const buildAggregatedRow = (title, rows) => {
406
+ const oldData = range.map((r) => rows.reduce((sum, row) => new decimal_js_1.default(sum).add(row[`minus_${r}`] ?? 0).toNumber(), 0));
407
+ const current = rows.reduce((sum, row) => new decimal_js_1.default(sum).add(row.amount).toNumber(), 0);
408
+ return [
409
+ { v: '', s: { font: { bold: true } } },
410
+ { v: title, s: { font: { italic: true } } },
411
+ ...oldData.map((v) => ({ v, t: 'n' })),
412
+ { v: current, t: 'n' },
413
+ ];
414
+ };
406
415
  await Promise.all([FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS, FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET].map(async (financialSpreadingType) => {
407
416
  const financialSpreadingData = await this.getFinancialSpreadingData({ ...params, financialSpreadingType }, monthDeep);
408
417
  const sheetsView = financialSpreadingData.sheets;
409
- const someData = financialSpreadingData.data.reduce((acc, rowData) => {
410
- const foundSheet = sheetsView.find((sheet) => sheet._id === rowData.sheetId);
411
- const oldData = range.reduce((acc, rangeValue) => [...acc, rowData[`minus_${rangeValue}`]], []);
412
- const totalStyle = foundSheet.isTotal ? rowStyles[foundSheet.rowType] ?? {} : {};
413
- const row = [
414
- { v: foundSheet.isTotal ? foundSheet.name : '', s: { font: { bold: true } } },
415
- { v: foundSheet.isTotal ? '' : foundSheet.name, s: { font: { italic: true } } },
416
- ...oldData.map((data) => ({ v: data, t: 'n' })),
417
- { v: rowData.amount, t: 'n' },
418
- ];
419
- const rowWithStyles = row.map((cell) => ({ ...cell, s: lodash_1.default.merge(cell.s, totalStyle) }));
420
- return [...acc, rowWithStyles];
421
- }, []);
422
- fullData[financialSpreadingType] = [fullHeaders, ...someData];
418
+ const data = financialSpreadingData.data;
419
+ const sheetById = lodash_1.default.keyBy(sheetsView, '_id');
420
+ const dataByRowType = lodash_1.default.groupBy(data, (row) => {
421
+ const sheet = sheetById[row.sheetId];
422
+ return sheet?.rowType;
423
+ });
424
+ const rows = [];
425
+ for (const [rowType, rowsData] of Object.entries(dataByRowType)) {
426
+ // ===== OPERATING EXPENSES (special case)
427
+ if (rowType === FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_EXPENSES) {
428
+ const fixed = rowsData.filter(r => sheetById[r.sheetId]?.isFixedCost);
429
+ const variable = rowsData.filter(r => !sheetById[r.sheetId]?.isFixedCost);
430
+ if (fixed.length) {
431
+ rows.push(buildAggregatedRow('Fixed operating expenses', fixed));
432
+ }
433
+ if (variable.length) {
434
+ rows.push(buildAggregatedRow('Variable operating expenses', variable));
435
+ }
436
+ continue;
437
+ }
438
+ // ===== DEFAULT ROW RENDERING
439
+ rowsData.forEach((rowData) => {
440
+ const sheet = sheetById[rowData.sheetId];
441
+ if (!sheet)
442
+ return;
443
+ const oldData = range.map(r => rowData[`minus_${r}`]);
444
+ const baseRow = [
445
+ { v: sheet.isTotal ? sheet.name : '', s: { font: { bold: true } } },
446
+ { v: sheet.isTotal ? '' : sheet.name, s: { font: { italic: true } } },
447
+ ...oldData.map(v => ({ v, t: 'n' })),
448
+ { v: rowData.amount, t: 'n' },
449
+ ];
450
+ const style = sheet.isTotal ? rowStyles[sheet.rowType] ?? {} : {};
451
+ rows.push(baseRow.map(cell => ({
452
+ ...cell,
453
+ s: lodash_1.default.merge(cell.s, style),
454
+ })));
455
+ });
456
+ }
457
+ fullData[financialSpreadingType] = [
458
+ fullHeaders,
459
+ ...rows,
460
+ ];
423
461
  }));
424
462
  return await this.uploadsService.convertDataToFileWithStyleOld({
425
463
  'PROFIT LOSS': fullData[FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS],
@@ -213,7 +213,7 @@ export class FinancialSpreadingService {
213
213
  order: sheet.order,
214
214
  suborder: sheet.suborder,
215
215
  rowType: sheet.rowType as EFinanceSpreadingPLTotal | EFinanceSpreadingBSTotal,
216
- }
216
+ };
217
217
  if (sheet.isFixedCost) {
218
218
  update.isFixedCost = sheet.isFixedCost;
219
219
  }
@@ -230,14 +230,12 @@ export class FinancialSpreadingService {
230
230
  return { success: false, data, sheets, message: 'Invalid data' };
231
231
  }
232
232
  await Promise.all(editableData.map(async (item) => {
233
- const { _id, isFixedCost, ...rest } = item;
233
+ const { _id, ...rest } = item;
234
234
 
235
235
  const update: Partial<IFinancialSpreading> = {
236
236
  amount: rest.amount,
237
237
  };
238
238
 
239
- console.debug({ _id, update });
240
-
241
239
  if (_id.includes('new_')) {
242
240
  if (rest.sheetId.includes('new_')) {
243
241
  rest.sheetId = sheetMap[rest.sheetId];
@@ -464,27 +462,90 @@ export class FinancialSpreadingService {
464
462
  s: { font: { bold: true } },
465
463
  }));
466
464
 
465
+ const buildAggregatedRow = (
466
+ title: string,
467
+ rows: IFinancialSpreadingViewWithDeepData[],
468
+ ): IExcelDataCellWithStyles[] => {
469
+ const oldData = range.map((r) =>
470
+ rows.reduce((sum, row) => new Decimal(sum).add(row[`minus_${r}`] as number ?? 0).toNumber(), 0),
471
+ );
472
+
473
+ const current = rows.reduce((sum, row) => new Decimal(sum).add(row.amount).toNumber(), 0);
474
+
475
+ return [
476
+ { v: '', s: { font: { bold: true } } },
477
+ { v: title, s: { font: { italic: true } } },
478
+ ...oldData.map((v) => ({ v, t: 'n' })),
479
+ { v: current, t: 'n' },
480
+ ];
481
+ };
482
+
467
483
  await Promise.all([EFinancialSpreadingType.PROFIT_LOSS, EFinancialSpreadingType.BALANCE_SHEET].map(async (financialSpreadingType) => {
468
484
  const financialSpreadingData = await this.getFinancialSpreadingData(
469
485
  { ...params, financialSpreadingType },
470
486
  monthDeep,
471
487
  );
472
488
 
473
- const sheetsView = financialSpreadingData.sheets as unknown[] as IFinancialSpreadingSheetView[];
474
- const someData = (financialSpreadingData.data as unknown as IFinancialSpreadingViewWithDeepData[]).reduce((acc, rowData) => {
475
- const foundSheet = sheetsView.find((sheet) => sheet._id === rowData.sheetId);
476
- const oldData = range.reduce((acc, rangeValue) => [...acc, rowData[`minus_${rangeValue}`]], []);
477
- const totalStyle = foundSheet.isTotal ? rowStyles[foundSheet.rowType] ?? {} : {};
478
- const row: IExcelDataCellWithStyles[] = [
479
- { v: foundSheet.isTotal ? foundSheet.name : '', s: { font: { bold: true } } },
480
- { v: foundSheet.isTotal ? '' : foundSheet.name, s: { font: { italic: true } } },
481
- ...oldData.map((data) => ({ v: data, t: 'n' })),
482
- { v: rowData.amount, t: 'n' },
483
- ];
484
- const rowWithStyles = row.map((cell) => ({ ...cell, s: _.merge(cell.s, totalStyle) }));
485
- return [...acc, rowWithStyles];
486
- }, []);
487
- fullData[financialSpreadingType] = [fullHeaders, ...someData];
489
+ const sheetsView = financialSpreadingData.sheets as IFinancialSpreadingSheetView[];
490
+ const data = financialSpreadingData.data as unknown as IFinancialSpreadingViewWithDeepData[];
491
+
492
+ const sheetById = _.keyBy(sheetsView, '_id');
493
+
494
+ const dataByRowType = _.groupBy(data, (row) => {
495
+ const sheet = sheetById[row.sheetId];
496
+ return sheet?.rowType;
497
+ });
498
+
499
+ const rows: IExcelDataCellWithStyles[][] = [];
500
+
501
+ for (const [rowType, rowsData] of Object.entries(dataByRowType)) {
502
+
503
+ // ===== OPERATING EXPENSES (special case)
504
+ if (rowType === EFinanceSpreadingPLTotal.OPERATING_EXPENSES) {
505
+
506
+ const fixed = rowsData.filter(r => sheetById[r.sheetId]?.isFixedCost);
507
+ const variable = rowsData.filter(r => !sheetById[r.sheetId]?.isFixedCost);
508
+
509
+ if (fixed.length) {
510
+ rows.push(buildAggregatedRow('Fixed operating expenses', fixed));
511
+ }
512
+
513
+ if (variable.length) {
514
+ rows.push(buildAggregatedRow('Variable operating expenses', variable));
515
+ }
516
+
517
+ continue;
518
+ }
519
+
520
+ // ===== DEFAULT ROW RENDERING
521
+ rowsData.forEach((rowData) => {
522
+ const sheet = sheetById[rowData.sheetId];
523
+ if (!sheet) return;
524
+
525
+ const oldData = range.map(r => rowData[`minus_${r}`]);
526
+
527
+ const baseRow: IExcelDataCellWithStyles[] = [
528
+ { v: sheet.isTotal ? sheet.name : '', s: { font: { bold: true } } },
529
+ { v: sheet.isTotal ? '' : sheet.name, s: { font: { italic: true } } },
530
+ ...oldData.map(v => ({ v, t: 'n' })),
531
+ { v: rowData.amount, t: 'n' },
532
+ ];
533
+
534
+ const style = sheet.isTotal ? rowStyles[sheet.rowType] ?? {} : {};
535
+
536
+ rows.push(
537
+ baseRow.map(cell => ({
538
+ ...cell,
539
+ s: _.merge(cell.s, style),
540
+ })),
541
+ );
542
+ });
543
+ }
544
+
545
+ fullData[financialSpreadingType] = [
546
+ fullHeaders,
547
+ ...rows,
548
+ ];
488
549
  }));
489
550
 
490
551
  return await this.uploadsService.convertDataToFileWithStyleOld({