@accounter/server
Version:
Accounter GraphQL server
221 lines • 9.03 kB
JavaScript
import { format, startOfMonth } from 'date-fns';
import { EntryType, pcnGenerator } from '../../../../../pcn874-generator/src/index.js';
import { dateToTimelessDateString, idValidator, yearMonthValidator, } from '../../../shared/helpers/index.js';
import { BusinessesProvider } from '../../financial-entities/providers/businesses.provider.js';
import { getVatRecords } from '../resolvers/get-vat-records.resolver.js';
export const getHeaderDataFromRecords = (transactions, licensedDealerId, reportMonth = '', generationDate) => {
let derivedReportMonth = reportMonth;
let taxableSalesAmount = 0;
let taxableSalesVat = 0;
let salesRecordCount = 0;
let zeroValOrExemptSalesCount = 0;
let otherInputsVat = 0;
let equipmentInputsVat = 0;
let inputsCount = 0;
for (const t of transactions) {
const invoiceSumFactor = t.invoiceSum >= 0 ? 1 : -1;
switch (t.entryType) {
case EntryType.SALE_REGULAR: {
taxableSalesVat += t.totalVat * invoiceSumFactor;
taxableSalesAmount += t.invoiceSum;
salesRecordCount += 1;
break;
}
case EntryType.SALE_UNIDENTIFIED_CUSTOMER: {
if (t.totalVat === 0) {
zeroValOrExemptSalesCount += t.invoiceSum;
}
else {
taxableSalesVat += t.totalVat * invoiceSumFactor;
taxableSalesAmount += t.invoiceSum;
}
salesRecordCount += 1;
break;
}
case EntryType.SALE_UNIDENTIFIED_ZERO_OR_EXEMPT: {
salesRecordCount += 1;
zeroValOrExemptSalesCount += t.invoiceSum;
break;
}
case EntryType.INPUT_REGULAR: {
if (t.isProperty) {
equipmentInputsVat += t.totalVat * invoiceSumFactor;
}
else {
otherInputsVat += t.totalVat * invoiceSumFactor;
}
inputsCount += 1;
break;
}
case EntryType.INPUT_PETTY_CASH: {
if (t.isProperty) {
equipmentInputsVat += t.totalVat * invoiceSumFactor;
}
else {
otherInputsVat += t.totalVat * invoiceSumFactor;
}
inputsCount += 1;
break;
}
default: {
console.debug(`Transaction EntryType ${t.entryType} is not implemented yet`);
}
}
if (t.invoiceDate.substring(0, 6) > derivedReportMonth) {
derivedReportMonth = t.invoiceDate.substring(0, 6);
}
}
const totalVat = taxableSalesVat - otherInputsVat - equipmentInputsVat;
const header = {
licensedDealerId,
reportMonth: reportMonth || derivedReportMonth,
generationDate,
taxableSalesAmount,
taxableSalesVat,
salesRecordCount,
zeroValOrExemptSalesCount,
otherInputsVat,
equipmentInputsVat,
inputsCount,
totalVat,
};
return header;
};
export const getEntryTypeByRecord = (entry) => {
if (entry.pcn874RecordType) {
switch (entry.pcn874RecordType) {
case EntryType.SALE_REGULAR:
return EntryType.SALE_REGULAR;
case EntryType.SALE_UNIDENTIFIED_ZERO_OR_EXEMPT:
return EntryType.SALE_UNIDENTIFIED_ZERO_OR_EXEMPT;
case EntryType.INPUT_REGULAR:
return EntryType.INPUT_REGULAR;
case EntryType.INPUT_PETTY_CASH:
return EntryType.INPUT_PETTY_CASH;
case EntryType.SALE_SELF_INVOICE:
return EntryType.SALE_SELF_INVOICE;
case EntryType.SALE_EXPORT:
return EntryType.SALE_EXPORT;
case EntryType.SALE_PALESTINIAN_CUSTOMER:
return EntryType.SALE_PALESTINIAN_CUSTOMER;
case EntryType.SALE_ZERO_OR_EXEMPT:
return EntryType.SALE_ZERO_OR_EXEMPT;
case EntryType.SALE_UNIDENTIFIED_CUSTOMER:
return EntryType.SALE_UNIDENTIFIED_CUSTOMER;
case EntryType.INPUT_IMPORT:
return EntryType.INPUT_IMPORT;
case EntryType.INPUT_PALESTINIAN_SUPPLIER:
return EntryType.INPUT_PALESTINIAN_SUPPLIER;
case EntryType.INPUT_SINGLE_DOC_BY_LAW:
return EntryType.INPUT_SINGLE_DOC_BY_LAW;
case EntryType.INPUT_SELF_INVOICE:
return EntryType.INPUT_SELF_INVOICE;
default:
throw new Error(`Unhandled PCN874 Record Type: ${entry.pcn874RecordType}`);
}
}
if (entry.isExpense) {
return EntryType.INPUT_REGULAR;
}
if ((entry.vatNumber ?? 0) === 0) {
if (Number(entry.foreignVatAfterDeduction ?? 0) === 0 && (entry.localVat ?? 0) === 0) {
return EntryType.SALE_UNIDENTIFIED_ZERO_OR_EXEMPT;
}
return EntryType.SALE_UNIDENTIFIED_CUSTOMER;
}
if (Number(entry.foreignVatAfterDeduction ?? 0) === 0 && (entry.localVat ?? 0) === 0) {
return EntryType.SALE_ZERO_OR_EXEMPT;
}
return EntryType.SALE_REGULAR;
};
const NO_VAT_ID_ENTRIES = [
EntryType.SALE_UNIDENTIFIED_CUSTOMER,
EntryType.SALE_UNIDENTIFIED_ZERO_OR_EXEMPT,
EntryType.INPUT_PETTY_CASH,
];
// TODO: migrate helper functions to PCN874 generator
export function getVatIdForTransaction(t) {
if (t.pcn874RecordType && NO_VAT_ID_ENTRIES.includes(t.pcn874RecordType)) {
return '0';
}
if (!t.vatNumber && t.pcn874RecordType === EntryType.INPUT_REGULAR) {
return '999999999';
}
return t.vatNumber ?? '0';
}
export function getReferenceForTransaction(t) {
if (t.pcn874RecordType === EntryType.INPUT_IMPORT) {
return '0';
}
if (t.pcn874RecordType === EntryType.INPUT_PETTY_CASH) {
return '1';
}
if (t.documentSerial) {
return t.documentSerial;
}
if (t.pcn874RecordType === EntryType.SALE_UNIDENTIFIED_CUSTOMER ||
t.pcn874RecordType === EntryType.SALE_UNIDENTIFIED_ZERO_OR_EXEMPT) {
return '1';
}
return '0';
}
export function getTotalVAT(t) {
if (t.pcn874RecordType === EntryType.SALE_ZERO_OR_EXEMPT ||
t.pcn874RecordType === EntryType.SALE_UNIDENTIFIED_ZERO_OR_EXEMPT ||
t.pcn874RecordType === EntryType.SALE_EXPORT) {
return 0;
}
return Math.round(Math.abs(Number(t.roundedVATToAdd ?? 0)));
}
const transactionsFromVatReportRecords = (vatRecords) => {
const transactions = [];
for (const t of vatRecords) {
if (!t.documentDate) {
console.debug(`Document ${t.documentId} has no tax_invoice_date. Skipping it.`);
continue;
}
const entryType = getEntryTypeByRecord(t);
const transaction = {
entryType,
vatId: getVatIdForTransaction(t),
invoiceDate: format(new Date(t.documentDate), 'yyyyMMdd'),
refGroup: '0000',
refNumber: getReferenceForTransaction(t),
totalVat: getTotalVAT(t),
invoiceSum: Math.round(Number(t.localAmountBeforeVAT)),
isProperty: t.isProperty,
allocationNumber: t.allocationNumber ?? undefined,
};
transactions.push(transaction);
}
return transactions.sort((a, b) => a.invoiceDate.localeCompare(b.invoiceDate));
};
export async function getPcn874String(injector, businessId, rawMonthDate) {
const monthDate = dateToTimelessDateString(startOfMonth(new Date(rawMonthDate)));
const financialEntity = await injector
.get(BusinessesProvider)
.getBusinessByIdLoader.load(businessId);
if (!financialEntity?.vat_number) {
throw new Error(`Business entity ${businessId} has no VAT number`);
}
const vatRecords = await getVatRecords({ filters: { monthDate, financialEntityId: businessId } }, injector);
const reportMonth = format(new Date(monthDate), 'yyyyMM');
const reportContent = generatePcnFromVatRecords([
...vatRecords.income,
...vatRecords.expenses,
], financialEntity.vat_number, reportMonth);
return { reportContent, monthDate, reportMonth, financialEntity };
}
const generatePcnFromVatRecords = (vatRecords, vatNumber, reportMonth) => {
if (!yearMonthValidator(reportMonth)) {
throw new Error(`Expected reportMonth to be legit date formed as YYYYMM, received "${reportMonth}"`);
}
if (!idValidator(vatNumber, 9)) {
throw new Error(`Expected vatNumber to be 9 digits, received "${vatNumber}"`);
}
const transactions = transactionsFromVatReportRecords(vatRecords);
const header = getHeaderDataFromRecords(transactions, vatNumber, reportMonth);
const reportContent = pcnGenerator(header, transactions, { strict: false });
return reportContent;
};
//# sourceMappingURL=pcn.helper.js.map