Files
bigcapital/packages/server/src/modules/SaleInvoices/SaleInvoiceCostGLEntries.ts
2025-04-07 11:51:24 +02:00

149 lines
4.5 KiB
TypeScript

import * as R from 'ramda';
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { TenantModelProxy } from '../System/models/TenantBaseModel';
import { InventoryCostLotTracker } from '../InventoryCost/models/InventoryCostLotTracker';
import { LedgerStorageService } from '../Ledger/LedgerStorage.service';
import { groupInventoryTransactionsByTypeId } from '../InventoryCost/utils';
import { Ledger } from '../Ledger/Ledger';
import { AccountNormal } from '@/interfaces/Account';
import { ILedgerEntry } from '../Ledger/types/Ledger.types';
import { increment } from '@/utils/increment';
@Injectable()
export class SaleInvoiceCostGLEntries {
constructor(
private readonly ledgerStorage: LedgerStorageService,
@Inject(InventoryCostLotTracker.name)
private readonly inventoryCostLotTracker: TenantModelProxy<
typeof InventoryCostLotTracker
>,
) {}
/**
* Writes journal entries from sales invoices.
* @param {Date} startingDate - Starting date.
* @param {boolean} override
*/
public writeInventoryCostJournalEntries = async (
startingDate: Date,
trx?: Knex.Transaction,
): Promise<void> => {
const inventoryCostLotTrans = await this.inventoryCostLotTracker()
.query()
.where('direction', 'OUT')
.where('transaction_type', 'SaleInvoice')
.where('cost', '>', 0)
.modify('filterDateRange', startingDate)
.orderBy('date', 'ASC')
.withGraphFetched('invoice')
.withGraphFetched('item');
const ledger = this.getInventoryCostLotsLedger(inventoryCostLotTrans);
// Commit the ledger to the storage.
await this.ledgerStorage.commit(ledger, trx);
};
/**
* Retrieves the inventory cost lots ledger.
* @param {IInventoryLotCost[]} inventoryCostLots
* @returns {Ledger}
*/
private getInventoryCostLotsLedger = (
inventoryCostLots: InventoryCostLotTracker[],
) => {
// Groups the inventory cost lots transactions.
const inventoryTransactions =
groupInventoryTransactionsByTypeId(inventoryCostLots);
const entries = inventoryTransactions
.map(this.getSaleInvoiceCostGLEntries)
.flat();
return new Ledger(entries);
};
/**
*
* @param {IInventoryLotCost} inventoryCostLot
* @returns {}
*/
private getInvoiceCostGLCommonEntry = (
inventoryCostLot: InventoryCostLotTracker,
) => {
return {
currencyCode: inventoryCostLot.invoice.currencyCode,
exchangeRate: inventoryCostLot.invoice.exchangeRate,
transactionType: inventoryCostLot.transactionType,
transactionId: inventoryCostLot.transactionId,
date: inventoryCostLot.date,
indexGroup: 20,
costable: true,
createdAt: inventoryCostLot.createdAt,
debit: 0,
credit: 0,
branchId: inventoryCostLot.invoice.branchId,
};
};
/**
* Retrieves the inventory cost GL entry.
* @param {IInventoryLotCost} inventoryLotCost
* @returns {ILedgerEntry[]}
*/
private getInventoryCostGLEntry = R.curry(
(
getIndexIncrement,
inventoryCostLot: InventoryCostLotTracker,
): ILedgerEntry[] => {
const commonEntry = this.getInvoiceCostGLCommonEntry(inventoryCostLot);
const costAccountId =
inventoryCostLot.costAccountId || inventoryCostLot.item.costAccountId;
// XXX Debit - Cost account.
const costEntry = {
...commonEntry,
debit: inventoryCostLot.cost,
accountId: costAccountId,
accountNormal: AccountNormal.DEBIT,
itemId: inventoryCostLot.itemId,
index: getIndexIncrement(),
};
// XXX Credit - Inventory account.
const inventoryEntry = {
...commonEntry,
credit: inventoryCostLot.cost,
accountId: inventoryCostLot.item.inventoryAccountId,
accountNormal: AccountNormal.DEBIT,
itemId: inventoryCostLot.itemId,
index: getIndexIncrement(),
};
return [costEntry, inventoryEntry];
},
);
/**
* Writes journal entries for given sale invoice.
* -----
* - Cost of goods sold -> Debit -> YYYY
* - Inventory assets -> Credit -> YYYY
*-----
* @param {ISaleInvoice} saleInvoice
* @param {JournalPoster} journal
*/
public getSaleInvoiceCostGLEntries = (
inventoryCostLots: InventoryCostLotTracker[],
): ILedgerEntry[] => {
const getIndexIncrement = increment(0);
const getInventoryLotEntry =
this.getInventoryCostGLEntry(getIndexIncrement);
return inventoryCostLots.map((t) => getInventoryLotEntry(t)).flat();
};
}