@tomei/finance
Version:
NestJS package for finance module
1,005 lines (1,004 loc) • 93.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = __importDefault(require("axios"));
const general_1 = require("@tomei/general");
const account_1 = __importDefault(require("../account/account"));
const journal_entry_1 = __importDefault(require("../journal-entry/journal-entry"));
const document_1 = __importDefault(require("../document/document"));
const finance_company_repository_1 = require("./finance-company.repository");
const finance_customer_repository_1 = require("../customer/finance-customer.repository");
const ledger_transaction_repository_1 = require("../ledger-transaction/ledger-transaction.repository");
const enum_1 = require("../enum");
const payment_method_type_1 = __importDefault(require("../payment-method-type/payment-method-type"));
const payment_repository_1 = require("../payment/payment.repository");
const payment_item_repository_1 = require("../payment-item/payment-item.repository");
const document_repository_1 = require("../document/document.repository");
const document_item_repository_1 = require("../document/document-item.repository");
const payment_method_repository_1 = require("../payment-method/payment-method.repository");
const payment_method_type_repository_1 = require("../payment-method-type/payment-method-type.repository");
const payment_method_1 = __importDefault(require("../payment-method/payment-method"));
const account_repository_1 = require("../account/account.repository");
const payment_paid_with_repository_1 = require("../payment-paid-with/payment-paid-with.repository");
const document_item_1 = __importDefault(require("../document/document-item"));
const typeof_1 = require("../helpers/typeof");
const activity_history_1 = require("@tomei/activity-history");
const config_1 = require("@tomei/config");
const tax_repository_1 = require("../tax/tax.repository");
const tax_1 = require("../tax/tax");
const collect_payment_type_1 = require("../enum/collect-payment-type");
const tag_1 = require("../tag/tag");
const tag_repository_1 = require("../tag/tag.repository");
const tag_group_repository_1 = require("../tag-group/tag-group.repository");
const tag_group_1 = require("../tag-group/tag-group");
const document_tag_repository_1 = require("../document-tag/document-tag.repository");
const document_tag_1 = require("../document-tag/document-tag");
const payment_method_type_entity_1 = __importDefault(require("../models/payment-method-type.entity"));
class FinanceCompany extends general_1.ObjectBase {
get ObjectType() {
return this._ObjectType;
}
get CompSystemCode() {
return this._CompSystemCode;
}
set CompSystemCode(code) {
this._CompSystemCode = code;
}
get CompSystemRefId() {
return this._CompSystemRefId;
}
set CompSystemRefId(id) {
this._CompSystemRefId = id;
}
get AccSystemCode() {
return this._AccSystemCode;
}
get AccSystemRefId() {
return this._AccSystemRefId;
}
set AccSystemRefId(id) {
this._AccSystemRefId = id;
}
get PostedToAccSystemYN() {
return this._PostedToAccSystemYN;
}
set PostedToAccSystemYN(value) {
this._PostedToAccSystemYN = value;
}
get PostedById() {
return this._PostedById;
}
set PostedById(id) {
this._PostedById = id;
}
get PostedDateTime() {
return this._PostedDateTime;
}
set PostedDateTime(date) {
this._PostedDateTime = date;
}
set AccSystemCode(code) {
this._AccSystemCode = code;
}
get CompanyId() {
return this._CompanyId;
}
get ObjectId() {
return this._CompanyId;
}
set ObjectId(id) {
this._CompanyId = id;
}
get ObjectName() {
return `${this.CompSystemCode}-${this.CompSystemRefId}-${this.AccSystemCode}`;
}
get TableName() {
return 'finance_Company';
}
get AccountingSystem() {
return this._AccountingSystem;
}
set AccountingSystem(system) {
this._AccountingSystem = system;
}
constructor(compSystemCode, compSystemRefId, accSystemCode) {
super();
this._CompanyId = 'New';
this._CompSystemCode = '';
this._CompSystemRefId = '';
this._AccSystemCode = 'REF';
this._AccSystemRefId = 'REF';
this._PostedToAccSystemYN = 'N';
this._PostedById = null;
this._PostedDateTime = null;
this._ObjectType = 'FinanceCompany';
this._PaymentMethods = [];
this._Taxes = [];
this.CompSystemCode = compSystemCode;
this.CompSystemRefId = compSystemRefId;
this.AccSystemCode = accSystemCode || 'REF';
this.AccSystemRefId = 'REF';
this.PostedToAccSystemYN = 'N';
}
static async getFinanceCompanyId(compSystemCode, compSystemRefId, accSystemCode, dbTransaction) {
let sCompanyId = '';
const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
if (!FinanceCompany._htFinanceCompanyIds.get(sKey)) {
const financeCompany = new FinanceCompany(compSystemCode, compSystemRefId, accSystemCode);
const company = await FinanceCompany._financeCompanyRepository.findOne({
where: {
CompSystemCode: compSystemCode,
CompSystemRefId: compSystemRefId,
AccSystemCode: accSystemCode,
},
transaction: dbTransaction,
});
financeCompany.ObjectId = company.CompanyId;
sCompanyId = financeCompany.ObjectId;
FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
FinanceCompany._htFinanceCompanies.add(sCompanyId, financeCompany);
}
if (typeof FinanceCompany._htFinanceCompanyIds.get(sKey) === 'string') {
sCompanyId = FinanceCompany._htFinanceCompanyIds.get(sKey);
}
return sCompanyId;
}
static async getFinanceCompany(companyId, dbTransaction) {
if (!FinanceCompany._htFinanceCompanies.get(companyId)) {
const company = await FinanceCompany._financeCompanyRepository.findOne({
where: { CompanyId: companyId },
transaction: dbTransaction,
});
if (!company) {
throw Error('No finance company found. Please create first.');
}
const compSystemCode = company.CompSystemCode;
const compSystemRefId = company.CompSystemRefId;
const accSystemCode = company.AccSystemCode;
const financeCompany = new FinanceCompany(compSystemCode, compSystemRefId, accSystemCode);
financeCompany.ObjectId = company.CompanyId;
financeCompany._CompanyId = company.CompanyId;
const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
FinanceCompany._htFinanceCompanies.add(financeCompany.ObjectId, financeCompany);
}
return FinanceCompany._htFinanceCompanies.get(companyId);
}
static async createFinanceCompany(dbTransaction, loginUser, companyId, compSystemCode, compSystemRefId, accSystemCode, accSystemRefId, postedToAccSystemYN) {
const financeCompany = new FinanceCompany(compSystemCode, compSystemRefId, accSystemCode);
const company = await FinanceCompany._financeCompanyRepository.findOne({
where: {
CompSystemCode: compSystemCode,
CompSystemRefId: compSystemRefId,
AccSystemCode: accSystemCode,
},
});
if (company) {
throw Error('There is already another Finance Company with the compSystemCode, CompSystemRefId and accSystemCode specified.');
}
financeCompany.ObjectId = companyId;
financeCompany.PostedById = loginUser?.ObjectId || null;
financeCompany.PostedDateTime = new Date();
if (postedToAccSystemYN === 'Y' && !financeCompany.PostedById) {
throw Error('Cannot post to the accounting system because no user is specified.');
}
await FinanceCompany._financeCompanyRepository.create({
CompanyId: financeCompany.CompanyId,
CompSystemCode: financeCompany.CompSystemCode,
CompSystemRefId: financeCompany.CompSystemRefId,
AccSystemCode: financeCompany.AccSystemCode,
AccSystemRefId: postedToAccSystemYN === 'Y'
? accSystemRefId
: financeCompany.AccSystemRefId,
PostedToAccSystemYN: postedToAccSystemYN || financeCompany.PostedToAccSystemYN,
PostedById: postedToAccSystemYN === 'Y' ? financeCompany.PostedById : null,
PostedDateTime: postedToAccSystemYN === 'Y' ? financeCompany.PostedDateTime : null,
}, {
transaction: dbTransaction,
});
const sKey = `${compSystemCode}-${compSystemRefId}-${accSystemCode}`;
FinanceCompany._htFinanceCompanyIds.add(sKey, financeCompany.ObjectId);
FinanceCompany._htFinanceCompanies.add(financeCompany.ObjectId, financeCompany);
console.log('return from hash table');
return FinanceCompany._htFinanceCompanies.get(companyId);
}
static async findAccount(loginUser, dbTransaction, accountNo) {
try {
const systemCode = config_1.ApplicationConfig.getComponentConfigValue('system-code');
const isPrivileged = await loginUser.checkPrivileges(systemCode, 'FINANCECOMPANY_VIEW_ACCOUNT');
if (!isPrivileged) {
throw new general_1.ClassError('FinanceCompany', 'findAccountErrMsg0X', 'User not privileged to view finance company account');
}
const record = await FinanceCompany._AccountRepository.findOne({
where: {
AccountNo: accountNo,
},
transaction: dbTransaction,
});
if (record) {
const account = new account_1.default(dbTransaction);
account.init(record.get({ plain: true }));
return account;
}
return undefined;
}
catch (error) {
throw error;
}
}
async createCustomer(dbTransaction, custSystemCode, custSystemRefId, customer, loginUser) {
try {
if (!custSystemCode || !custSystemRefId) {
throw new Error('CustSystemCode and CustomerRefId are required fields.');
}
const financeCustomerData = await FinanceCompany._FinanceCustomerRepository.findOne({
where: {
CompanyId: this._CompanyId,
CustSystemCode: custSystemCode,
CustSystemRefId: custSystemRefId,
},
});
let AccCustomerRefId = 'REF';
if (!customer.AccSystemRefId || customer.AccSystemRefId == 'REF') {
if (this.AccountingSystem) {
AccCustomerRefId = await this.AccountingSystem.createCustomer(customer, dbTransaction);
customer.PostedById = loginUser.ObjectId;
customer.PostedDateTime = new Date();
customer.PostedToAccSystemYN = 'Y';
}
}
customer.CompanyId = this._CompanyId;
let customerRecord;
let isUpdated = false;
if (financeCustomerData) {
customerRecord = await customer.update(AccCustomerRefId ? AccCustomerRefId : undefined, dbTransaction);
isUpdated = true;
}
else {
customerRecord = await customer.save(AccCustomerRefId, custSystemCode, custSystemRefId, dbTransaction);
isUpdated = false;
}
const activity = new activity_history_1.Activity();
activity.ActivityId = this._createId().toUpperCase();
if (isUpdated) {
activity.Action = activity_history_1.ActionEnum.UPDATE;
activity.Description = 'Update Finance Customer';
}
else {
activity.Action = activity_history_1.ActionEnum.CREATE;
activity.Description = 'Add Finance Customer';
}
activity.EntityType = 'FinanceCustomer';
activity.EntityId = customerRecord.CustomerId;
activity.EntityValueBefore = JSON.stringify({});
activity.EntityValueAfter = JSON.stringify({
CustomerId: customerRecord.CustomerId,
CustSystemCode: customerRecord.CustSystemCode,
CustSystemRefId: customerRecord.CustSystemRefId,
Name: customerRecord.Name,
Description: customerRecord.Description,
CreatedAt: customerRecord.CreatedAt,
CreatedById: customerRecord.CreatedById,
UpdatedAt: customerRecord.UpdatedAt,
UpdatedById: customerRecord.UpdatedById,
AccSystemRefId: customerRecord.AccSystemRefId,
PostedToAccSystemYN: customerRecord.PostedToAccSystemYN,
PostedById: customerRecord.PostedById,
PostedDateTime: customerRecord.PostedDateTime,
});
await activity.create(loginUser.ObjectId, dbTransaction);
return customer;
}
catch (error) {
throw error;
}
}
async postJournal(dbTransaction, journalEntry, loginUser) {
const debitTransactions = await journalEntry.DebitTransactions;
const creditTransactions = await journalEntry.CreditTransactions;
try {
if (creditTransactions.length < 1 || debitTransactions?.length < 1) {
throw new Error('There should be at least 1 debit ledger transaction and 1 credit ledger transaction in the journal entry');
}
let totalCreditAmount = creditTransactions.reduce((accumulator, currentValue) => accumulator + currentValue.CreditAmount, 0);
let totalDebitAmount = debitTransactions.reduce((accumulator, currentValue) => accumulator + currentValue.DebitAmount, 0);
console.log('totalCreditAmount: ', totalCreditAmount);
console.log('totalDebitAmount: ', totalDebitAmount);
if (typeof totalCreditAmount === 'string') {
totalCreditAmount = parseFloat(totalCreditAmount);
}
if (typeof totalDebitAmount === 'string') {
totalDebitAmount = parseFloat(totalDebitAmount);
}
if (totalCreditAmount.toFixed(2) !== totalDebitAmount.toFixed(2)) {
throw new Error('Credit ledger transaction and debit ledger transaction should the same amount');
}
const newJournalEntry = await journalEntry.save(loginUser.ObjectId, dbTransaction);
for (const ledgerTransaction of debitTransactions) {
ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
await ledgerTransaction.save(dbTransaction);
}
for (const ledgerTransaction of creditTransactions) {
ledgerTransaction.JournalEntryId = newJournalEntry.JournalEntryId;
await ledgerTransaction.save(dbTransaction);
}
const payload = {
Action: 'Create',
Activity: 'Post Journal Entry',
Description: `Journal Entry (ID: ${newJournalEntry.JournalEntryId}) has been created`,
EntityType: 'JournalEntry',
EntityValueBefore: JSON.stringify({}),
EntityValueAfter: JSON.stringify(newJournalEntry),
PerformedById: loginUser.ObjectId,
PerformedAt: new Date(),
EntityId: newJournalEntry.JournalEntryId,
};
await axios_1.default.post(`${process.env.COMMON_API_URL}/activity-histories`, payload);
}
catch (error) {
throw error;
}
}
async createAccount(dbTransaction, account, loginUser) {
try {
let accSystemRefId;
if (!account.AccountType) {
throw new Error('AccountType is required.');
}
const accountData = await FinanceCompany._AccountRepository.findByPk(account.AccountNo, {
transaction: dbTransaction,
});
if (accountData) {
account.AccountNo = account.AccountNo
? account.AccountNo
: accountData.AccountNo;
account.CompanyId = account.CompanyId
? account.CompanyId
: accountData.CompanyId;
account.ParentAccountNo = account.ParentAccountNo
? account.ParentAccountNo
: accountData.ParentAccountNo;
account.Name = account.Name ? account.Name : accountData.Name;
account.Description = account.Description
? account.Description
: accountData.Description;
account.AccountType = account.AccountType
? account.AccountType
: accountData.AccountType;
account.AccountSubtype = account.AccountSubtype
? account.AccountSubtype
: accountData.AccountSubType;
account.OwnerId = account.OwnerId
? account.OwnerId
: accountData.OwnerId;
account.OwnerType = account.OwnerType
? account.OwnerType
: accountData.OwnerType;
account.RelatedObjectId = account.RelatedObjectId
? account.RelatedObjectId
: accountData.RelatedObjectId;
account.RelatedObjectType = account.RelatedObjectType
? account.RelatedObjectType
: accountData.RelatedObjectType;
account.CreatedById = account.CreatedById
? account.CreatedById
: accountData.CreatedById;
account.CreatedAt = account.CreatedAt
? account.CreatedAt
: accountData.CreatedAt;
account.UpdatedById = account.UpdatedById
? account.UpdatedById
: accountData.UpdatedById;
account.UpdatedAt = account.UpdatedAt
? account.UpdatedAt
: accountData.UpdatedAt;
account.AccSystemRefId =
account.AccSystemRefId && account.AccSystemRefId !== 'REF'
? account.AccSystemRefId
: accountData.AccSystemRefId;
account.PostedToAccSystemYN =
account.PostedToAccSystemYN !== 'N'
? account.PostedToAccSystemYN
: accountData.PostedToAccSystemYN;
account.PostedById = account.PostedById
? account.PostedById
: accountData.PostedById;
account.PostedDateTime = account.PostedDateTime
? account.PostedDateTime
: accountData.PostedDateTime;
}
let createAccountPayload = {
Name: account.Name,
AcctNum: account.AccountNo,
AccountType: account.AccountType,
AccountSubType: account.AccountSubtype,
};
if (this.AccountingSystem) {
accSystemRefId = await this.AccountingSystem.createAccount(account, dbTransaction);
}
if (account.isParentAccountExists()) {
createAccountPayload = {
...createAccountPayload,
CurrencyRef: 'MYR',
ParentRef: account.ParentAccountNo,
SubAccount: true,
};
}
console.log('Finance Company Create Account: Before accSystemAccountId Create');
console.log('Finance Company Create Account: After accSystemAccountId Create');
console.log('Finance Company Create Account: Before new Account Create');
if (account.AccSystemRefId && account.AccSystemRefId !== 'REF') {
account.PostedToAccSystemYN = 'Y';
account.PostedById = loginUser.ObjectId;
account.PostedDateTime = new Date();
}
const newAccount = await account.save(this.CompanyId, account.AccSystemRefId || 'REF', loginUser.ObjectId, dbTransaction);
console.log('Finance Company Create Account: After new Account Create');
const activity = new activity_history_1.Activity();
activity.ActivityId = this._createId().toUpperCase();
if (accountData) {
activity.Action = activity_history_1.ActionEnum.UPDATE;
activity.Description = 'Update Finance Account';
}
else {
activity.Action = activity_history_1.ActionEnum.CREATE;
activity.Description = 'Add Finance Account';
}
activity.EntityType = 'Account';
activity.EntityId = account.AccountNo;
activity.EntityValueBefore = JSON.stringify({});
activity.EntityValueAfter = JSON.stringify({
AccountNo: newAccount.AccountNo,
Name: newAccount.Name,
Description: newAccount.Description,
AccountType: newAccount.AccountType,
AccountSubtype: newAccount.AccountSubtype,
CreatedAt: newAccount.CreatedAt,
CreatedById: newAccount.CreatedById,
UpdatedAt: newAccount.UpdatedAt,
UpdatedById: newAccount.UpdatedById,
AccSystemRefId: newAccount.AccSystemRefId,
PostedToAccSystemYN: newAccount.PostedToAccSystemYN,
PostedById: newAccount.PostedById,
PostedDateTime: newAccount.PostedDateTime,
});
await activity.create(loginUser.ObjectId, dbTransaction);
return newAccount;
}
catch (error) {
throw error;
}
}
async issueInvoice(dbTransaction, invoice, loginUser, customer, dtAccountNo, tags, discount) {
try {
const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne({
where: {
DocNo: invoice.DocNo,
},
transaction: dbTransaction,
});
if (duplicateInvoice) {
throw new Error('Invoice number already exists');
}
const documentItems = await invoice.getDocumentItems(dbTransaction);
if (!documentItems.length) {
throw new Error('Document must have at least 1 document item');
}
for (const invoiceItem of documentItems) {
if (!invoiceItem.CtAccountNo) {
throw new Error('Each document item should have CtAccountNo provided');
}
}
invoice.DocType = enum_1.DocType.INVOICE;
let invoiceMedia;
if (invoice.UseAccSystemDocYN === 'Y') {
let payload = {
customer: customer,
invoice: invoice,
paymentMode: 'credit',
discount: discount,
};
if (tags) {
payload = {
...payload,
tagIds: tags.map((tag) => tag.AccSystemRefId),
};
}
const accInvoiceRefIdDetail = await this.AccountingSystem.createInvoice(payload, dbTransaction);
invoice.DocNo = accInvoiceRefIdDetail.invoiceNo;
invoice.AccSystemRefId = accInvoiceRefIdDetail.invoiceRefId;
invoice.PostedToAccSystemYN = 'Y';
invoice.PostedById = loginUser.ObjectId;
invoice.PostedDateTime = new Date();
for (const documentItem of documentItems) {
const accItem = accInvoiceRefIdDetail.invoiceItems.find((item) => item.name === documentItem.Name);
if (accItem) {
documentItem.DocNo = accInvoiceRefIdDetail.invoiceNo;
documentItem.AccSystemRefId = accItem.itemRefId;
documentItem.PostedToAccSystemYN = 'Y';
documentItem.PostedById = loginUser.ObjectId;
documentItem.PostedDateTime = new Date();
}
}
}
else {
invoiceMedia = await invoice.generateInvoice(invoice.IssuedById, customer, dbTransaction);
}
await FinanceCompany._DocumentRepository.create({
DocNo: invoice.DocNo,
DocType: invoice.DocType,
DocDate: invoice.DocDate,
CompanyId: invoice.CompanyId,
Currency: invoice.Currency,
AmountBeforeDiscount: invoice.AmountBeforeDiscount,
Amount: invoice.Amount,
DiscountType: invoice.DiscountType,
DiscountValue: invoice.DiscountValue,
Description: invoice.Description,
Status: invoice.Status,
IssuedById: invoice.IssuedById,
IssuedToId: invoice.IssuedToId,
IssuedToType: invoice.IssuedToType,
RelatedObjectId: invoice.RelatedObjectId,
RelatedObjectType: invoice.RelatedObjectType,
CreatedById: invoice.CreatedById,
CreatedAt: new Date(),
UpdatedById: invoice.UpdatedById,
UpdatedAt: new Date(),
DocPDFFileMediaId: invoice.UseAccSystemDocYN == 'N'
? invoiceMedia.PDFMedia.MediaId
: null,
DocHTMLFileMediaId: invoice.UseAccSystemDocYN == 'N'
? invoiceMedia.HTMLMedia.MediaId
: null,
AccSystemRefId: invoice.AccSystemRefId,
PostedToAccSystemYN: invoice.PostedToAccSystemYN,
PostedById: invoice.UseAccSystemDocYN == 'Y' ? invoice.PostedById : null,
PostedDateTime: new Date(),
UseAccSystemDocYN: invoice.UseAccSystemDocYN,
}, {
transaction: dbTransaction,
});
for (const documentItem of documentItems) {
await FinanceCompany._DocumentItemRepository.create({
DocumentItemId: this._createId().toUpperCase(),
DocNo: invoice.DocNo,
Name: documentItem.Name,
NameBM: documentItem.NameBM,
Description: documentItem.Description,
ItemId: documentItem.ItemId,
ItemType: documentItem.ItemType,
ItemSKU: documentItem.ItemSKU,
ItemSerialNo: documentItem.ItemSerialNo,
Currency: documentItem.Currency,
UnitPrice: documentItem.UnitPrice,
Quantity: documentItem.Quantity,
QuantityUOM: documentItem.QuantityUOM,
AmountBeforeDiscount: documentItem.AmountBeforeDiscount,
Amount: documentItem.Amount,
DiscountType: documentItem.DiscountType,
DiscountValue: documentItem.DiscountValue,
TaxCode: documentItem.TaxCode,
TaxAmount: documentItem.TaxAmount,
TaxRate: documentItem.TaxRate,
TaxInclusiveYN: documentItem.TaxInclusiveYN,
DtAccountNo: documentItem.DtAccountNo
? documentItem.DtAccountNo
: null,
CtAccountNo: documentItem.CtAccountNo
? documentItem.CtAccountNo
: null,
AccSystemRefId: documentItem.AccSystemRefId,
PostedToAccSystemYN: documentItem.PostedToAccSystemYN,
PostedById: documentItem.PostedById,
PostedDateTime: documentItem.PostedDateTime,
}, {
transaction: dbTransaction,
});
}
if (tags) {
const documentTagRepo = new document_tag_repository_1.DocumentTagRepository();
for (const tag of tags) {
const documentTag = await document_tag_1.DocumentTag.init(dbTransaction, documentTagRepo);
documentTag.DocNo = invoice.DocNo;
documentTag.TagId = tag.TagId;
await documentTag.create(loginUser, dbTransaction);
}
}
const transactionDate = new Date();
const htCreditAccountAmount = new general_1.HashTable();
const htCreditAccountCurrency = new general_1.HashTable();
const htCreditAccountPurpose = new general_1.HashTable();
documentItems.forEach((invoiceItem) => {
if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
htCreditAccountAmount.add(invoiceItem.CtAccountNo, invoiceItem.Amount);
htCreditAccountCurrency.add(invoiceItem.CtAccountNo, invoiceItem.Currency);
htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
}
else {
const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
htCreditAccountAmount.add(invoiceItem.CtAccountNo, d + invoiceItem.Amount);
}
});
const savedItems = htCreditAccountAmount.list();
for (const item of savedItems) {
const journalEntry = new journal_entry_1.default(dbTransaction);
journalEntry.init({
CompanyId: this.CompanyId,
Name: 'Issue Invoice ' + invoice.DocNo,
});
const creditAmount = item.value[1];
const currency = htCreditAccountCurrency.get(item.value[0]);
const purpose = htCreditAccountPurpose.get(item.value[0]);
const dt = await journalEntry.newLedgerTransaction(enum_1.TransactionTypeOptions.DEBIT);
if (dtAccountNo) {
dt.AccountNo = dtAccountNo;
}
else {
const arAccount = await customer.getAccountReceivable();
dt.AccountNo = arAccount.AccountNo;
}
dt.Currency = currency ? currency : 'MYR';
dt.DebitAmount = creditAmount ? creditAmount : 0.0;
dt.Date = transactionDate;
dt.Description = `${purpose}`;
dt.Name = `${purpose}`;
dt.RelatedDocNo = invoice.DocNo;
dt.RelatedObjectId = invoice.RelatedObjectId;
dt.RelatedObjectType = invoice.RelatedObjectType;
const ct = await journalEntry.newLedgerTransaction(enum_1.TransactionTypeOptions.CREDIT);
ct.AccountNo = item.value[0];
ct.Currency = currency ? currency : 'MYR';
ct.CreditAmount = creditAmount ? creditAmount : 0.0;
ct.Date = transactionDate;
ct.Description = customer.FullName;
ct.Name = customer.FullName;
ct.RelatedDocNo = invoice.DocNo;
ct.RelatedObjectId = invoice.RelatedObjectId;
ct.RelatedObjectType = invoice.RelatedObjectType;
await this.postJournal(dbTransaction, journalEntry, loginUser);
}
return invoice;
}
catch (err) {
console.log('Issue invoice err: ', err);
throw err;
}
}
static async findCustomer(custSystemRefId, dbTransaction) {
const data = await FinanceCompany._FinanceCustomerRepository.findOne({
where: {
CustSystemRefId: custSystemRefId,
},
transaction: dbTransaction,
});
return data;
}
async issueDebitNote(dbTransaction, loginUser, invoice, customer, dtAccountNo) {
try {
const duplicateInvoice = await FinanceCompany._DocumentRepository.findOne({
where: {
DocNo: invoice.DocNo,
},
transaction: dbTransaction,
});
if (duplicateInvoice) {
throw new Error('Invoice number already exists');
}
const documentItems = await invoice.getDocumentItems(dbTransaction);
if (!documentItems.length) {
throw new Error('Document must have at least 1 document item');
}
for (const invoiceItem of documentItems) {
if (!invoiceItem.CtAccountNo) {
throw new Error('Each document item should have CtAccountNo provided');
}
}
invoice.DocType = enum_1.DocType.DEBIT_NOTE;
if (invoice.UseAccSystemDocYN === 'Y') {
const accInvoiceRefIDetail = await this.AccountingSystem.createInvoice({
customer,
invoice,
paymentMode: 'credit',
}, dbTransaction);
invoice.AccSystemRefId = accInvoiceRefIDetail.invoiceRefId;
invoice.PostedToAccSystemYN = 'Y';
invoice.PostedById = loginUser.ObjectId;
invoice.PostedDateTime = new Date();
for (const documentItem of documentItems) {
const accItem = accInvoiceRefIDetail.invoiceItems.find((item) => item.name === documentItem.Name);
if (accItem) {
documentItem.AccSystemRefId = accItem.itemRefId;
documentItem.PostedToAccSystemYN = 'Y';
documentItem.PostedById = loginUser.ObjectId;
documentItem.PostedDateTime = new Date();
}
}
}
else {
invoice.generateInvoice(loginUser.IDNo, customer, dbTransaction);
}
await FinanceCompany._DocumentRepository.create({
DocNo: invoice.DocNo,
DocType: invoice.DocType,
DocDate: invoice.DocDate,
CompanyId: invoice.CompanyId,
Currency: invoice.Currency,
AmountBeforeDiscount: invoice.AmountBeforeDiscount,
Amount: invoice.Amount,
DiscountType: invoice.DiscountType,
DiscountValue: invoice.DiscountValue,
Description: invoice.Description,
Status: invoice.Status,
IssuedById: loginUser.ObjectId,
IssuedToId: customer.ObjectId,
IssuedToType: invoice.IssuedToType,
RelatedObjectId: invoice.RelatedObjectId,
RelatedObjectType: invoice.RelatedObjectType,
CreatedById: loginUser.ObjectId,
CreatedAt: new Date(),
UpdatedById: loginUser.ObjectId,
UpdatedAt: new Date(),
DocPDFFileMediaId: invoice.DocPDFFileMediaId,
DocHTMLFileMediaId: invoice.DocHTMLFileMediaId,
AccSystemRefId: invoice.AccSystemRefId,
PostedToAccSystemYN: invoice.PostedToAccSystemYN,
PostedById: invoice.PostedToAccSystemYN == 'Y' ? invoice.PostedById : null,
PostedDateTime: new Date(),
UseAccSystemDocYN: invoice.UseAccSystemDocYN,
}, {
transaction: dbTransaction,
});
documentItems.forEach(async (documentItem) => {
await FinanceCompany._DocumentItemRepository.create({
DocumentItemId: this._createId().toUpperCase(),
DocNo: documentItem.DocNo,
Name: documentItem.Name,
NameBM: documentItem.NameBM,
Description: documentItem.Description,
ItemId: documentItem.ItemId,
ItemType: documentItem.ItemType,
ItemSKU: documentItem.ItemSKU,
ItemSerialNo: documentItem.ItemSerialNo,
Currency: documentItem.Currency,
UnitPrice: documentItem.UnitPrice,
Quantity: documentItem.Quantity,
QuantityUOM: documentItem.QuantityUOM,
AmountBeforeDiscount: documentItem.AmountBeforeDiscount,
Amount: documentItem.Amount,
DiscountType: documentItem.DiscountType,
DiscountValue: documentItem.DiscountValue,
TaxCode: documentItem.TaxCode,
TaxAmount: documentItem.TaxAmount,
TaxRate: documentItem.TaxRate,
TaxInclusiveYN: documentItem.TaxInclusiveYN,
DtAccountNo: documentItem.DtAccountNo,
CtAccountNo: documentItem.CtAccountNo,
AccSystemRefId: documentItem.AccSystemRefId,
PostedToAccSystemYN: documentItem.PostedToAccSystemYN,
PostedById: documentItem.PostedById,
PostedDateTime: documentItem.PostedDateTime,
}, {
transaction: dbTransaction,
});
});
const transactionDate = new Date();
const htCreditAccountAmount = new general_1.HashTable();
const htCreditAccountCurrency = new general_1.HashTable();
const htCreditAccountPurpose = new general_1.HashTable();
documentItems.forEach((invoiceItem) => {
if (!htCreditAccountAmount.exists(invoiceItem.CtAccountNo)) {
htCreditAccountAmount.add(invoiceItem.CtAccountNo, invoiceItem.Amount);
htCreditAccountCurrency.add(invoiceItem.CtAccountNo, invoiceItem.Currency);
htCreditAccountPurpose.add(invoiceItem.CtAccountNo, invoiceItem.Name);
}
else {
const d = htCreditAccountAmount.get(invoiceItem.CtAccountNo);
htCreditAccountAmount.add(invoiceItem.CtAccountNo, d + invoiceItem.Amount);
}
});
const savedItems = htCreditAccountAmount.list();
for (const item of savedItems) {
const journalEntry = new journal_entry_1.default(dbTransaction);
journalEntry.init({
CompanyId: this.CompanyId,
Name: 'issue Invoice ' + invoice.DocNo,
});
const creditAmount = item.value[1];
const currency = htCreditAccountCurrency.get(item.value[0]);
const purpose = htCreditAccountPurpose.get(item.value[0]);
const dt = await journalEntry.newLedgerTransaction(enum_1.TransactionTypeOptions.DEBIT);
if (dtAccountNo) {
dt.AccountNo = dtAccountNo;
}
else {
const arAccount = await customer.getAccountReceivable();
dt.AccountNo = arAccount.AccountNo;
}
dt.Currency = currency ? currency : 'MYR';
dt.DebitAmount = creditAmount ? creditAmount : 0.0;
dt.Date = transactionDate;
dt.Description = `${purpose}`;
dt.Name = `${purpose}`;
dt.RelatedDocNo = invoice.DocNo;
dt.RelatedObjectId = invoice.RelatedObjectId;
dt.RelatedObjectType = invoice.RelatedObjectType;
const ct = await journalEntry.newLedgerTransaction(enum_1.TransactionTypeOptions.CREDIT);
ct.AccountNo = item.value[0];
ct.Currency = currency ? currency : 'MYR';
ct.CreditAmount = creditAmount ? creditAmount : 0.0;
ct.Date = transactionDate;
ct.Description = customer.FullName;
ct.Name = customer.FullName;
ct.RelatedDocNo = invoice.DocNo;
ct.RelatedObjectId = invoice.RelatedObjectId;
ct.RelatedObjectType = invoice.RelatedObjectType;
await this.postJournal(dbTransaction, journalEntry, loginUser);
}
return invoice;
}
catch (err) {
console.log('Issue debit note err: ', err);
throw err;
}
}
async issueCreditNote(dbTransaction, loginUser, creditNote, customer, ctAccountNo) {
try {
const duplicateCreditNote = await FinanceCompany._DocumentRepository.findOne({
where: {
DocNo: creditNote.DocNo,
},
transaction: dbTransaction,
});
if (duplicateCreditNote) {
throw new Error('Invoice number already exists');
}
const documentItems = await creditNote.getDocumentItems(dbTransaction);
if (!documentItems.length) {
throw new Error('Document must have at least 1 document item');
}
for (const invoiceItem of documentItems) {
if (!invoiceItem.DtAccountNo) {
throw new Error('Each document item should have DtAccountNo provided');
}
const actualDocument = await document_1.default.initDocument(dbTransaction, invoiceItem.ItemId);
if (actualDocument.IssuedToId !== creditNote.IssuedToId) {
throw new general_1.ClassError('FinanceCompany', 'FinanceCompanyErrMsgOX', 'To issue credit note, all invoices must belong to same customer.');
}
}
if (creditNote.UseAccSystemDocYN === 'Y' && this.AccountingSystem) {
const creditNoteItems = await this.getAccSystemCreditNoteItems(dbTransaction, documentItems);
const payload = {
customer: {
customerId: customer.AccSystemRefId,
name: customer.FullName,
email: customer.Email,
contactNo: customer.ContactNo,
},
referenceInvoices: [],
issueDate: creditNote.DocDate.toISOString(),
currency: creditNote.Currency,
reason: creditNote.Description,
items: creditNoteItems,
createdBy: {
userId: loginUser.ObjectId,
name: '',
},
};
const postedCreditNote = await this.AccountingSystem.createCreditNote(payload, dbTransaction);
creditNote.DocNo = postedCreditNote.creditNoteNo;
creditNote.AccSystemRefId = postedCreditNote.creditNoteRefId;
creditNote.PostedToAccSystemYN = 'Y';
creditNote.PostedById = loginUser.ObjectId;
creditNote.PostedDateTime = new Date();
}
else {
creditNote.generateCreditNote(loginUser.IDNo, customer);
}
creditNote.DocType = enum_1.DocType.CREDIT_NOTE;
creditNote.Status = enum_1.DocumentStatus.SETTLED;
const creditNotedDocumentPayload = {
DocNo: creditNote.DocNo,
DocType: creditNote.DocType,
DocDate: creditNote.DocDate,
CompanyId: creditNote.CompanyId,
Currency: creditNote.Currency,
AmountBeforeDiscount: creditNote.AmountBeforeDiscount,
Amount: creditNote.Amount,
DiscountType: creditNote.DiscountType,
DiscountValue: creditNote.DiscountValue,
Description: creditNote.Description,
Status: creditNote.Status,
IssuedById: creditNote.IssuedById
? creditNote.IssuedById
: loginUser.ObjectId,
IssuedToId: creditNote.IssuedToId
? creditNote.IssuedToId
: customer.CustomerId,
IssuedToType: creditNote.IssuedToType
? creditNote.IssuedToType
: 'FinanceCustomer',
RelatedObjectId: creditNote.RelatedObjectId,
RelatedObjectType: creditNote.RelatedObjectType,
CreatedById: loginUser.ObjectId,
CreatedAt: new Date(),
UpdatedById: loginUser.ObjectId,
UpdatedAt: new Date(),
DocPDFFileMediaId: creditNote.DocPDFFileMediaId,
DocHTMLFileMediaId: creditNote.DocHTMLFileMediaId,
AccSystemRefId: creditNote.AccSystemRefId,
PostedToAccSystemYN: creditNote.PostedToAccSystemYN,
PostedById: creditNote.PostedToAccSystemYN == 'Y' ? creditNote.PostedById : null,
PostedDateTime: creditNote.PostedToAccSystemYN == 'Y' ? new Date() : null,
UseAccSystemDocYN: creditNote.UseAccSystemDocYN,
};
await FinanceCompany._DocumentRepository.create(creditNotedDocumentPayload, {
transaction: dbTransaction,
});
const activity = new activity_history_1.Activity();
activity.ActivityId = activity.createId();
activity.Action = activity_history_1.ActionEnum.CREATE;
activity.Description = 'Create Credit Note ' + creditNote.DocNo;
activity.EntityType = 'Document';
activity.EntityId = creditNote.DocNo;
activity.EntityValueBefore = JSON.stringify({});
activity.EntityValueAfter = JSON.stringify(creditNotedDocumentPayload);
await activity.create(loginUser.ObjectId, dbTransaction);
for (const docItem of documentItems) {
const documentItemPayload = {
DocumentItemId: this._createId().toUpperCase(),
DocNo: creditNote.DocNo,
Name: docItem.Name,
NameBM: docItem.NameBM,
Description: docItem.Description,
ItemId: docItem.ItemId,
ItemType: docItem.ItemType,
ItemSKU: docItem.ItemSKU,
ItemSerialNo: docItem.ItemSerialNo,
Currency: docItem.Currency,
UnitPrice: docItem.UnitPrice,
Quantity: docItem.Quantity,
QuantityUOM: docItem.QuantityUOM,
AmountBeforeDiscount: docItem.AmountBeforeDiscount,
Amount: docItem.Amount,
DiscountType: docItem.DiscountType,
DiscountValue: docItem.DiscountValue,
TaxCode: docItem.TaxCode,
TaxAmount: docItem.TaxAmount,
TaxRate: docItem.TaxRate,
TaxInclusiveYN: docItem.TaxInclusiveYN,
DtAccountNo: docItem.DtAccountNo ? docItem.DtAccountNo : null,
CtAccountNo: docItem.CtAccountNo ? docItem.CtAccountNo : null,
AccSystemRefId: docItem.AccSystemRefId,
PostedToAccSystemYN: docItem.PostedToAccSystemYN,
PostedById: docItem.PostedById,
PostedDateTime: docItem.PostedDateTime,
};
await FinanceCompany._DocumentItemRepository.create(documentItemPayload, {
transaction: dbTransaction,
});
const activity = new activity_history_1.Activity();
activity.ActivityId = activity.createId();
activity.Action = activity_history_1.ActionEnum.CREATE;
activity.Description =
'Create Document Item for Credit Note ' + creditNote.DocNo;
activity.EntityType = 'DocumentItem';
activity.EntityId = documentItemPayload.DocumentItemId;
activity.EntityValueBefore = JSON.stringify({});
activity.EntityValueAfter = JSON.stringify(documentItemPayload);
await activity.create(loginUser.ObjectId, dbTransaction);
await document_1.default.settleByCreditNote(loginUser, dbTransaction, docItem.ItemId, docItem.Amount);
}
const journalEntry = new journal_entry_1.default(dbTransaction);
journalEntry.init({
CompanyId: this.CompanyId,
Name: 'Issue Credit Note ' + creditNote.DocNo,
});
const transactionDate = new Date();
const creditTransaction = await journalEntry.newLedgerTransaction(enum_1.TransactionTypeOptions.CREDIT);
if (ctAccountNo) {
creditTransaction.AccountNo = ctAccountNo;
}
else {