Compare commits

..

6 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
04b16a7d2f Publish
- @bigcapital/server@0.10.1
 - @bigcapital/webapp@0.10.2
2023-10-03 22:07:42 +02:00
Ahmed Bouhuolia
fd27136274 tst 2023-10-03 22:06:33 +02:00
Ahmed Bouhuolia
951e360405 chore(webapp): add missing packages 2023-10-03 22:05:57 +02:00
Ahmed Bouhuolia
d2242b7744 WIP 2023-10-03 12:11:55 +02:00
Ahmed Bouhuolia
4e9b0ae24f fix: migrate from npm to pnpm 2023-09-29 15:54:47 +02:00
Ahmed Bouhuolia
e5ffb5661a feat: migrate to pnpm 2023-09-27 22:44:54 +02:00
143 changed files with 785 additions and 17171 deletions

View File

@@ -60,15 +60,6 @@
"contributions": [ "contributions": [
"bug" "bug"
] ]
},
{
"login": "kochie",
"name": "Robert Koch",
"avatar_url": "https://avatars.githubusercontent.com/u/10809884?v=4",
"profile": "https://me.kochie.io",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@@ -80,7 +80,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://scheibling.se"><img src="https://avatars.githubusercontent.com/u/24367830?v=4?s=100" width="100px;" alt="Lars Scheibling"/><br /><sub><b>Lars Scheibling</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Ascheibling" title="Bug reports">🐛</a></td> <td align="center" valign="top" width="14.28%"><a href="https://scheibling.se"><img src="https://avatars.githubusercontent.com/u/24367830?v=4?s=100" width="100px;" alt="Lars Scheibling"/><br /><sub><b>Lars Scheibling</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Ascheibling" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/suhaibaffan"><img src="https://avatars.githubusercontent.com/u/18115937?v=4?s=100" width="100px;" alt="Suhaib Affan"/><br /><sub><b>Suhaib Affan</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=suhaibaffan" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/suhaibaffan"><img src="https://avatars.githubusercontent.com/u/18115937?v=4?s=100" width="100px;" alt="Suhaib Affan"/><br /><sub><b>Suhaib Affan</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=suhaibaffan" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KalliopiPliogka"><img src="https://avatars.githubusercontent.com/u/81677549?v=4?s=100" width="100px;" alt="Kalliopi Pliogka"/><br /><sub><b>Kalliopi Pliogka</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3AKalliopiPliogka" title="Bug reports">🐛</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/KalliopiPliogka"><img src="https://avatars.githubusercontent.com/u/81677549?v=4?s=100" width="100px;" alt="Kalliopi Pliogka"/><br /><sub><b>Kalliopi Pliogka</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3AKalliopiPliogka" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://me.kochie.io"><img src="https://avatars.githubusercontent.com/u/10809884?v=4?s=100" width="100px;" alt="Robert Koch"/><br /><sub><b>Robert Koch</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=kochie" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -2,6 +2,5 @@
"$schema": "node_modules/lerna/schemas/lerna-schema.json", "$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "independent", "version": "independent",
"npmClient": "pnpm", "npmClient": "pnpm",
"useWorkspaces": true,
"packages": ["packages/*"] "packages": ["packages/*"]
} }

View File

@@ -2,6 +2,7 @@
"name": "bigcapital-monorepo", "name": "bigcapital-monorepo",
"private": true, "private": true,
"scripts": { "scripts": {
"bootstrap": "lerna exec pnpm install",
"dev": "lerna run dev", "dev": "lerna run dev",
"build": "lerna run build", "build": "lerna run build",
"dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\"", "dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\"",
@@ -22,7 +23,7 @@
"lerna": "^6.4.1" "lerna": "^6.4.1"
}, },
"engines": { "engines": {
"node": "16.x || 17.x || 18.x" "node": "14.x"
}, },
"husky": { "husky": {
"hooks": { "hooks": {

View File

@@ -1,4 +1,4 @@
FROM node:18.16.0-alpine as build FROM node:14.20-alpine as build
USER root USER root
@@ -83,25 +83,15 @@ WORKDIR /app
RUN chown node:node / RUN chown node:node /
# Install pnpm
RUN npm install -g pnpm
# Copy application dependency manifests to the container image. # Copy application dependency manifests to the container image.
COPY ./package*.json ./ COPY ./package*.json ./
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
COPY ./lerna.json ./lerna.json
COPY ./packages/server/package*.json ./packages/server/ COPY ./packages/server/package*.json ./packages/server/
# Install application dependencies COPY ./lerna.json ./lerna.json
RUN apk update
RUN apk add python3 build-base chromium
# Set PYHTON env # Install app dependencies for production.
ENV PYTHON=/usr/bin/python3 RUN npm install
RUN npm run bootstrap
# Install packages dependencies for production.
RUN pnpm install
COPY --chown=node:node ./packages/server ./packages/server COPY --chown=node:node ./packages/server ./packages/server

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bigcapital/server", "name": "@bigcapital/server",
"version": "0.10.2", "version": "0.10.1",
"description": "", "description": "",
"main": "src/server.ts", "main": "src/server.ts",
"scripts": { "scripts": {
@@ -43,7 +43,7 @@
"crypto-random-string": "^3.2.0", "crypto-random-string": "^3.2.0",
"csurf": "^1.10.0", "csurf": "^1.10.0",
"deep-map": "^2.0.0", "deep-map": "^2.0.0",
"deepdash": "^5.3.9", "deepdash": "^5.3.7",
"dotenv": "^8.1.0", "dotenv": "^8.1.0",
"errorhandler": "^1.5.1", "errorhandler": "^1.5.1",
"es6-weak-map": "^2.0.3", "es6-weak-map": "^2.0.3",
@@ -95,7 +95,6 @@
"rate-limiter-flexible": "^2.1.14", "rate-limiter-flexible": "^2.1.14",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rtl-detect": "^1.0.4", "rtl-detect": "^1.0.4",
"source-map-loader": "^4.0.1",
"ts-transformer-keys": "^0.4.2", "ts-transformer-keys": "^0.4.2",
"tsyringe": "^4.3.0", "tsyringe": "^4.3.0",
"typedi": "^0.8.0", "typedi": "^0.8.0",

View File

@@ -587,7 +587,6 @@
"balance_sheet.long_term_liabilities": "Long-Term Liabilities", "balance_sheet.long_term_liabilities": "Long-Term Liabilities",
"balance_sheet.non_current_liabilities": "Non-Current Liabilities", "balance_sheet.non_current_liabilities": "Non-Current Liabilities",
"balance_sheet.equity": "Equity", "balance_sheet.equity": "Equity",
"balance_sheet.net_income": "Net Income",
"balance_sheet.account_name": "Account name", "balance_sheet.account_name": "Account name",
"balance_sheet.total": "Total", "balance_sheet.total": "Total",

View File

@@ -16,7 +16,7 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
/** /**
* Router constructor. * Router constructor.
*/ */
public router() { router() {
const router = Router(); const router = Router();
router.get( router.get(
@@ -36,7 +36,7 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
* Validation schema. * Validation schema.
* @return {ValidationChain[]} * @return {ValidationChain[]}
*/ */
private get trialBalanceSheetValidationSchema(): ValidationChain[] { get trialBalanceSheetValidationSchema(): ValidationChain[] {
return [ return [
...this.sheetNumberFormatValidationSchema, ...this.sheetNumberFormatValidationSchema,
query('basis').optional(), query('basis').optional(),
@@ -59,37 +59,28 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
/** /**
* Retrieve the trial balance sheet. * Retrieve the trial balance sheet.
*/ */
private async trialBalanceSheet( public async trialBalanceSheet(
req: Request, req: Request,
res: Response, res: Response,
next: NextFunction next: NextFunction
) { ) {
const { tenantId } = req; const { tenantId, settings } = req;
let filter = this.matchedQueryData(req); let filter = this.matchedQueryData(req);
filter = { filter = {
...filter, ...filter,
accountsIds: castArray(filter.accountsIds), accountsIds: castArray(filter.accountsIds),
}; };
try {
const accept = this.accepts(req);
const acceptType = accept.types(['json', 'application/json+table']);
if (acceptType === 'application/json+table') { try {
const { table, meta, query } = const { data, query, meta } =
await this.trialBalanceSheetService.trialBalanceSheetTable( await this.trialBalanceSheetService.trialBalanceSheet(tenantId, filter);
tenantId,
filter return res.status(200).send({
); data: this.transfromToResponse(data),
return res.status(200).send({ table, meta, query }); query: this.transfromToResponse(query),
} else { meta: this.transfromToResponse(meta),
const { data, query, meta } = });
await this.trialBalanceSheetService.trialBalanceSheet(
tenantId,
filter
);
return res.status(200).send({ data, query, meta });
}
} catch (error) { } catch (error) {
next(error); next(error);
} }

View File

@@ -149,11 +149,6 @@ export default class ItemsController extends BaseController {
.trim() .trim()
.escape() .escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }), .isLength({ max: DATATYPES_LENGTH.TEXT }),
check('sell_tax_rate_id').optional({ nullable: true }).isInt().toInt(),
check('purchase_tax_rate_id')
.optional({ nullable: true })
.isInt()
.toInt(),
check('category_id') check('category_id')
.optional({ nullable: true }) .optional({ nullable: true })
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 }) .isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })
@@ -513,28 +508,6 @@ export default class ItemsController extends BaseController {
], ],
}); });
} }
if (error.errorType === 'PURCHASE_TAX_RATE_NOT_FOUND') {
return res.status(400).send({
errors: [
{
type: 'PURCHASE_TAX_RATE_NOT_FOUND',
message: 'Purchase tax rate has not found.',
code: 410,
},
],
});
}
if (error.errorType === 'SELL_TAX_RATE_NOT_FOUND') {
return res.status(400).send({
errors: [
{
type: 'SELL_TAX_RATE_NOT_FOUND',
message: 'Sell tax rate is not found.',
code: 420,
},
],
});
}
} }
next(error); next(error);
} }

View File

@@ -31,14 +31,14 @@ export default class OrganizationController extends BaseController {
router.post( router.post(
'/build', '/build',
this.buildOrganizationValidationSchema, this.organizationValidationSchema,
this.validationResult, this.validationResult,
asyncMiddleware(this.build.bind(this)), asyncMiddleware(this.build.bind(this)),
this.handleServiceErrors.bind(this) this.handleServiceErrors.bind(this)
); );
router.put( router.put(
'/', '/',
this.updateOrganizationValidationSchema, this.organizationValidationSchema,
this.validationResult, this.validationResult,
this.asyncMiddleware(this.updateOrganization.bind(this)), this.asyncMiddleware(this.updateOrganization.bind(this)),
this.handleServiceErrors.bind(this) this.handleServiceErrors.bind(this)
@@ -55,7 +55,7 @@ export default class OrganizationController extends BaseController {
* Organization setup schema. * Organization setup schema.
* @return {ValidationChain[]} * @return {ValidationChain[]}
*/ */
private get commonOrganizationValidationSchema(): ValidationChain[] { private get organizationValidationSchema(): ValidationChain[] {
return [ return [
check('name').exists().trim(), check('name').exists().trim(),
check('industry').optional({ nullable: true }).isString().trim().escape(), check('industry').optional({ nullable: true }).isString().trim().escape(),
@@ -68,29 +68,6 @@ export default class OrganizationController extends BaseController {
]; ];
} }
/**
* Build organization validation schema.
* @returns {ValidationChain[]}
*/
private get buildOrganizationValidationSchema(): ValidationChain[] {
return [...this.commonOrganizationValidationSchema];
}
/**
* Update organization validation schema.
* @returns {ValidationChain[]}
*/
private get updateOrganizationValidationSchema(): ValidationChain[] {
return [
...this.commonOrganizationValidationSchema,
check('tax_number')
.optional({ nullable: true })
.isString()
.trim()
.escape(),
];
}
/** /**
* Builds tenant database and migrate database schema. * Builds tenant database and migrate database schema.
* @param {Request} req - Express request. * @param {Request} req - Express request.

View File

@@ -115,8 +115,6 @@ export default class BillsController extends BaseController {
check('note').optional().trim().escape(), check('note').optional().trim().escape(),
check('open').default(false).isBoolean().toBoolean(), check('open').default(false).isBoolean().toBoolean(),
check('is_inclusive_tax').default(false).isBoolean().toBoolean(),
check('entries').isArray({ min: 1 }), check('entries').isArray({ min: 1 }),
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),
@@ -139,15 +137,6 @@ export default class BillsController extends BaseController {
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toInt(), .toInt(),
check('entries.*.tax_code')
.optional({ nullable: true })
.trim()
.escape()
.isString(),
check('entries.*.tax_rate_id')
.optional({ nullable: true })
.isNumeric()
.toInt(),
]; ];
} }
@@ -553,16 +542,6 @@ export default class BillsController extends BaseController {
], ],
}); });
} }
if (error.errorType === 'ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND') {
return res.boom.badRequest(null, {
errors: [{ type: 'ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND', code: 1800 }],
});
}
if (error.errorType === 'ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND') {
return res.boom.badRequest(null, {
errors: [{ type: 'ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND', code: 1900 }],
});
}
} }
next(error); next(error);
} }

View File

@@ -1,10 +0,0 @@
exports.up = (knex) => {
return knex.schema.table('bills', (table) => {
table.boolean('is_inclusive_tax').defaultTo(false);
table.decimal('tax_amount_withheld');
});
};
exports.down = (knex) => {
return knex.schema.table('bills', () => {});
};

View File

@@ -1,18 +0,0 @@
exports.up = (knex) => {
return knex.schema.table('items', (table) => {
table
.integer('sell_tax_rate_id')
.unsigned()
.references('id')
.inTable('tax_rates');
table
.integer('purchase_tax_rate_id')
.unsigned()
.references('id')
.inTable('tax_rates');
});
};
exports.down = (knex) => {
return knex.schema.dropTableIfExists('tax_rates');
};

View File

@@ -9,7 +9,6 @@ export enum BALANCE_SHEET_SCHEMA_NODE_TYPE {
AGGREGATE = 'AGGREGATE', AGGREGATE = 'AGGREGATE',
ACCOUNTS = 'ACCOUNTS', ACCOUNTS = 'ACCOUNTS',
ACCOUNT = 'ACCOUNT', ACCOUNT = 'ACCOUNT',
NET_INCOME = 'NET_INCOME',
} }
export enum BALANCE_SHEET_NODE_TYPE { export enum BALANCE_SHEET_NODE_TYPE {
@@ -34,7 +33,6 @@ export enum BALANCE_SHEET_SCHEMA_NODE_ID {
LOGN_TERM_LIABILITY = 'LOGN_TERM_LIABILITY', LOGN_TERM_LIABILITY = 'LOGN_TERM_LIABILITY',
NON_CURRENT_LIABILITY = 'NON_CURRENT_LIABILITY', NON_CURRENT_LIABILITY = 'NON_CURRENT_LIABILITY',
EQUITY = 'EQUITY', EQUITY = 'EQUITY',
NET_INCOME = 'NET_INCOME',
} }
// Balance sheet query. // Balance sheet query.
@@ -89,6 +87,7 @@ export interface IBalanceSheetDOO {
meta: IBalanceSheetMeta; meta: IBalanceSheetMeta;
} }
export interface IBalanceSheetCommonNode { export interface IBalanceSheetCommonNode {
total: IBalanceSheetTotal; total: IBalanceSheetTotal;
horizontalTotals?: IBalanceSheetTotal[]; horizontalTotals?: IBalanceSheetTotal[];
@@ -109,7 +108,7 @@ export interface IBalanceSheetAggregateNode extends IBalanceSheetCommonNode {
id: string; id: string;
name: string; name: string;
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE; nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE;
children?: IBalanceSheetDataNode[]; children?: (IBalanceSheetAggregateNode | IBalanceSheetAccountNode)[];
} }
export interface IBalanceSheetTotal { export interface IBalanceSheetTotal {
@@ -119,13 +118,6 @@ export interface IBalanceSheetTotal {
date?: string | Date; date?: string | Date;
} }
export interface IBalanceSheetAccountsNode extends IBalanceSheetCommonNode {
id: number | string;
name: string;
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS;
children: IBalanceSheetAccountNode[];
}
export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode { export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode {
id: number; id: number;
index: number; index: number;
@@ -136,17 +128,7 @@ export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode {
children?: IBalanceSheetAccountNode[]; children?: IBalanceSheetAccountNode[];
} }
export interface IBalanceSheetNetIncomeNode extends IBalanceSheetCommonNode { export type IBalanceSheetDataNode = IBalanceSheetAggregateNode;
id: number;
name: string;
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME;
}
export type IBalanceSheetDataNode =
| IBalanceSheetAggregateNode
| IBalanceSheetAccountNode
| IBalanceSheetAccountsNode
| IBalanceSheetNetIncomeNode;
export interface IBalanceSheetPercentageAmount { export interface IBalanceSheetPercentageAmount {
amount: number; amount: number;
@@ -168,16 +150,9 @@ export interface IBalanceSheetSchemaAccountNode {
accountsTypes: string[]; accountsTypes: string[];
} }
export interface IBalanceSheetSchemaNetIncomeNode {
id: string;
name: string;
type: BALANCE_SHEET_SCHEMA_NODE_TYPE;
}
export type IBalanceSheetSchemaNode = export type IBalanceSheetSchemaNode =
| IBalanceSheetSchemaAccountNode | IBalanceSheetSchemaAccountNode
| IBalanceSheetSchemaAggregateNode | IBalanceSheetSchemaAggregateNode;
| IBalanceSheetSchemaNetIncomeNode;
export interface IBalanceSheetDatePeriods { export interface IBalanceSheetDatePeriods {
assocAccountNodeDatePeriods(node): any; assocAccountNodeDatePeriods(node): any;

View File

@@ -2,7 +2,6 @@ import { Knex } from 'knex';
import { IDynamicListFilterDTO } from './DynamicFilter'; import { IDynamicListFilterDTO } from './DynamicFilter';
import { IItemEntry, IItemEntryDTO } from './ItemEntry'; import { IItemEntry, IItemEntryDTO } from './ItemEntry';
import { IBillLandedCost } from './LandedCost'; import { IBillLandedCost } from './LandedCost';
export interface IBillDTO { export interface IBillDTO {
vendorId: number; vendorId: number;
billNumber: string; billNumber: string;
@@ -16,10 +15,10 @@ export interface IBillDTO {
exchangeRate?: number; exchangeRate?: number;
open: boolean; open: boolean;
entries: IItemEntryDTO[]; entries: IItemEntryDTO[];
branchId?: number; branchId?: number;
warehouseId?: number; warehouseId?: number;
projectId?: number; projectId?: number;
isInclusiveTax?: boolean;
} }
export interface IBillEditDTO { export interface IBillEditDTO {
@@ -81,15 +80,6 @@ export interface IBill {
localAmount?: number; localAmount?: number;
locatedLandedCosts?: IBillLandedCost[]; locatedLandedCosts?: IBillLandedCost[];
amountLocal: number;
subtotal: number;
subtotalLocal: number;
subtotalExcludingTax: number;
taxAmountWithheld: number;
taxAmountWithheldLocal: number;
total: number;
totalLocal: number;
} }
export interface IBillsFilter extends IDynamicListFilterDTO { export interface IBillsFilter extends IDynamicListFilterDTO {

View File

@@ -22,9 +22,6 @@ export interface IItem {
sellDescription: string; sellDescription: string;
purchaseDescription: string; purchaseDescription: string;
sellTaxRateId: number;
purchaseTaxRateId: number;
quantityOnHand: number; quantityOnHand: number;
note: string; note: string;
@@ -57,9 +54,6 @@ export interface IItemDTO {
sellDescription: string; sellDescription: string;
purchaseDescription: string; purchaseDescription: string;
sellTaxRateId: number;
purchaseTaxRateId: number;
quantityOnHand: number; quantityOnHand: number;
note: string; note: string;

View File

@@ -7,25 +7,18 @@ export interface ILedger {
filter(cb: (entry: ILedgerEntry) => boolean): ILedger; filter(cb: (entry: ILedgerEntry) => boolean): ILedger;
whereAccountId(accountId: number): ILedger; whereAccountId(accountId: number): ILedger;
whereAccountsIds(accountsIds: number[]): ILedger;
whereContactId(contactId: number): ILedger; whereContactId(contactId: number): ILedger;
whereFromDate(fromDate: Date | string): ILedger; whereFromDate(fromDate: Date | string): ILedger;
whereToDate(toDate: Date | string): ILedger; whereToDate(toDate: Date | string): ILedger;
whereCurrencyCode(currencyCode: string): ILedger; whereCurrencyCode(currencyCode: string): ILedger;
whereBranch(branchId: number): ILedger; whereBranch(branchId: number): ILedger;
whereItem(itemId: number): ILedger; whereItem(itemId: number): ILedger;
whereProject(projectId: number): ILedger;
getClosingBalance(): number; getClosingBalance(): number;
getForeignClosingBalance(): number; getForeignClosingBalance(): number;
getClosingDebit(): number;
getClosingCredit(): number;
getContactsIds(): number[]; getContactsIds(): number[];
getAccountsIds(): number[]; getAccountsIds(): number[];
reverse(): ILedger;
isEmpty(): boolean;
} }
export interface ILedgerEntry { export interface ILedgerEntry {

View File

@@ -25,7 +25,6 @@ export interface IOrganizationUpdateDTO {
timezone: string; timezone: string;
fiscalYear: string; fiscalYear: string;
industry: string; industry: string;
taxNumber: string;
} }
export interface IOrganizationBuildEventPayload { export interface IOrganizationBuildEventPayload {

View File

@@ -36,7 +36,6 @@ export interface ITaxRateCreatedPayload {
} }
export interface ITaxRateEditingPayload { export interface ITaxRateEditingPayload {
oldTaxRate: ITaxRate;
editTaxRateDTO: IEditTaxRateDTO; editTaxRateDTO: IEditTaxRateDTO;
tenantId: number; tenantId: number;
trx: Knex.Transaction; trx: Knex.Transaction;

View File

@@ -33,7 +33,6 @@ export interface ITrialBalanceAccount extends ITrialBalanceTotal {
id: number; id: number;
parentAccountId: number; parentAccountId: number;
name: string; name: string;
formattedName: string;
code: string; code: string;
accountNormal: string; accountNormal: string;
} }

View File

@@ -81,9 +81,6 @@ import { ProjectBillableBillSubscriber } from '@/services/Projects/Projects/Proj
import { SyncActualTimeTaskSubscriber } from '@/services/Projects/Times/SyncActualTimeTaskSubscriber'; import { SyncActualTimeTaskSubscriber } from '@/services/Projects/Times/SyncActualTimeTaskSubscriber';
import { SaleInvoiceTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/SaleInvoiceTaxRateValidateSubscriber'; import { SaleInvoiceTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/SaleInvoiceTaxRateValidateSubscriber';
import { WriteInvoiceTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteInvoiceTaxTransactionsSubscriber'; import { WriteInvoiceTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteInvoiceTaxTransactionsSubscriber';
import { BillTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/BillTaxRateValidateSubscriber';
import { WriteBillTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteBillTaxTransactionsSubscriber';
import { SyncItemTaxRateOnEditTaxSubscriber } from '@/services/TaxRates/SyncItemTaxRateOnEditTaxSubscriber';
export default () => { export default () => {
return new EventPublisher(); return new EventPublisher();
@@ -191,14 +188,8 @@ export const susbcribers = () => {
ProjectBillableExpensesSubscriber, ProjectBillableExpensesSubscriber,
ProjectBillableBillSubscriber, ProjectBillableBillSubscriber,
// Tax Rates - Sale Invoice // Tax Rates
SaleInvoiceTaxRateValidateSubscriber, SaleInvoiceTaxRateValidateSubscriber,
WriteInvoiceTaxTransactionsSubscriber, WriteInvoiceTaxTransactionsSubscriber,
// Tax Rates - Bills
BillTaxRateValidateSubscriber,
WriteBillTaxTransactionsSubscriber,
SyncItemTaxRateOnEditTaxSubscriber
]; ];
}; };

View File

@@ -587,7 +587,6 @@
"balance_sheet.long_term_liabilities": "Long-Term Liabilities", "balance_sheet.long_term_liabilities": "Long-Term Liabilities",
"balance_sheet.non_current_liabilities": "Non-Current Liabilities", "balance_sheet.non_current_liabilities": "Non-Current Liabilities",
"balance_sheet.equity": "Equity", "balance_sheet.equity": "Equity",
"balance_sheet.net_income": "Net Income",
"balance_sheet.account_name": "Account name", "balance_sheet.account_name": "Account name",
"balance_sheet.total": "Total", "balance_sheet.total": "Total",

View File

@@ -13,109 +13,6 @@ export default class Bill extends mixin(TenantModel, [
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable, ModelSearchable,
]) { ]) {
public amount: number;
public paymentAmount: number;
public landedCostAmount: number;
public allocatedCostAmount: number;
public isInclusiveTax: boolean;
public taxAmountWithheld: number;
public exchangeRate: number;
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [
'balance',
'dueAmount',
'isOpen',
'isPartiallyPaid',
'isFullyPaid',
'isPaid',
'remainingDays',
'overdueDays',
'isOverdue',
'unallocatedCostAmount',
'localAmount',
'localAllocatedCostAmount',
'billableAmount',
'amountLocal',
'subtotal',
'subtotalLocal',
'subtotalExludingTax',
'taxAmountWithheldLocal',
'total',
'totalLocal',
];
}
/**
* Invoice amount in base currency.
* @returns {number}
*/
get amountLocal() {
return this.amount * this.exchangeRate;
}
/**
* Subtotal. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Subtotal in base currency. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotalLocal() {
return this.amountLocal;
}
/**
* Sale invoice amount excluding tax.
* @returns {number}
*/
get subtotalExcludingTax() {
return this.isInclusiveTax
? this.subtotal - this.taxAmountWithheld
: this.subtotal;
}
/**
* Tax amount withheld in base currency.
* @returns {number}
*/
get taxAmountWithheldLocal() {
return this.taxAmountWithheld * this.exchangeRate;
}
/**
* Invoice total. (Tax included)
* @returns {number}
*/
get total() {
return this.isInclusiveTax
? this.subtotal
: this.subtotal + this.taxAmountWithheld;
}
/**
* Invoice total in local currency. (Tax included)
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/** /**
* Table name * Table name
*/ */
@@ -261,13 +158,40 @@ export default class Bill extends mixin(TenantModel, [
}; };
} }
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [
'balance',
'dueAmount',
'isOpen',
'isPartiallyPaid',
'isFullyPaid',
'isPaid',
'remainingDays',
'overdueDays',
'isOverdue',
'unallocatedCostAmount',
'localAmount',
'localAllocatedCostAmount',
'billableAmount',
];
}
/** /**
* Invoice amount in organization base currency. * Invoice amount in organization base currency.
* @deprecated
* @returns {number} * @returns {number}
*/ */
get localAmount() { get localAmount() {
return this.amountLocal; return this.amount * this.exchangeRate;
} }
/** /**
@@ -307,7 +231,7 @@ export default class Bill extends mixin(TenantModel, [
* @return {number} * @return {number}
*/ */
get dueAmount() { get dueAmount() {
return Math.max(this.total - this.balance, 0); return Math.max(this.amount - this.balance, 0);
} }
/** /**
@@ -323,7 +247,7 @@ export default class Bill extends mixin(TenantModel, [
* @return {boolean} * @return {boolean}
*/ */
get isPartiallyPaid() { get isPartiallyPaid() {
return this.dueAmount !== this.total && this.dueAmount > 0; return this.dueAmount !== this.amount && this.dueAmount > 0;
} }
/** /**
@@ -384,7 +308,7 @@ export default class Bill extends mixin(TenantModel, [
* Retrieves the calculated amount which have not been invoiced. * Retrieves the calculated amount which have not been invoiced.
*/ */
get billableAmount() { get billableAmount() {
return Math.max(this.total - this.invoicedAmount, 0); return Math.max(this.amount - this.invoicedAmount, 0);
} }
/** /**
@@ -402,7 +326,6 @@ export default class Bill extends mixin(TenantModel, [
const ItemEntry = require('models/ItemEntry'); const ItemEntry = require('models/ItemEntry');
const BillLandedCost = require('models/BillLandedCost'); const BillLandedCost = require('models/BillLandedCost');
const Branch = require('models/Branch'); const Branch = require('models/Branch');
const TaxRateTransaction = require('models/TaxRateTransaction');
return { return {
vendor: { vendor: {
@@ -450,21 +373,6 @@ export default class Bill extends mixin(TenantModel, [
to: 'branches.id', to: 'branches.id',
}, },
}, },
/**
* Bill may has associated tax rate transactions.
*/
taxes: {
relation: Model.HasManyRelation,
modelClass: TaxRateTransaction.default,
join: {
from: 'bills.id',
to: 'tax_rate_transactions.referenceId',
},
filter(builder) {
builder.where('reference_type', 'Bill');
},
},
}; };
} }

View File

@@ -65,7 +65,6 @@ export default class Item extends mixin(TenantModel, [
const ItemEntry = require('models/ItemEntry'); const ItemEntry = require('models/ItemEntry');
const WarehouseTransferEntry = require('models/WarehouseTransferEntry'); const WarehouseTransferEntry = require('models/WarehouseTransferEntry');
const InventoryAdjustmentEntry = require('models/InventoryAdjustmentEntry'); const InventoryAdjustmentEntry = require('models/InventoryAdjustmentEntry');
const TaxRate = require('models/TaxRate');
return { return {
/** /**
@@ -179,35 +178,11 @@ export default class Item extends mixin(TenantModel, [
to: 'media.id', to: 'media.id',
}, },
}, },
/**
* Item may has sell tax rate.
*/
sellTaxRate: {
relation: Model.BelongsToOneRelation,
modelClass: TaxRate.default,
join: {
from: 'items.sellTaxRateId',
to: 'tax_rates.id',
},
},
/**
* Item may has purchase tax rate.
*/
purchaseTaxRate: {
relation: Model.BelongsToOneRelation,
modelClass: TaxRate.default,
join: {
from: 'items.purchaseTaxRateId',
to: 'tax_rates.id',
},
},
}; };
} }
/** /**
* *
*/ */
static get secureDeleteRelations() { static get secureDeleteRelations() {
return [ return [

View File

@@ -99,13 +99,6 @@ export default class ItemEntry extends TenantModel {
: getExlusiveTaxAmount(this.amount, this.taxRate); : getExlusiveTaxAmount(this.amount, this.taxRate);
} }
static calcAmount(itemEntry) {
const { discount, quantity, rate } = itemEntry;
const total = quantity * rate;
return discount ? total - total * discount * 0.01 : total;
}
/** /**
* Item entry relations. * Item entry relations.
*/ */

View File

@@ -1,5 +1,5 @@
import moment from 'moment'; import moment from 'moment';
import { defaultTo, sumBy, uniqBy } from 'lodash'; import { defaultTo, uniqBy } from 'lodash';
import { IAccountTransaction, ILedger, ILedgerEntry } from '@/interfaces'; import { IAccountTransaction, ILedger, ILedgerEntry } from '@/interfaces';
export default class Ledger implements ILedger { export default class Ledger implements ILedger {
@@ -49,15 +49,6 @@ export default class Ledger implements ILedger {
return this.filter((entry) => entry.accountId === accountId); return this.filter((entry) => entry.accountId === accountId);
} }
/**
* Filters entries by the given accounts ids then returns a new ledger.
* @param {number[]} accountIds
* @returns {ILedger}
*/
public whereAccountsIds(accountIds: number[]): ILedger {
return this.filter((entry) => accountIds.indexOf(entry.accountId) !== -1);
}
/** /**
* Filters entries that before or same the given date and returns a new ledger. * Filters entries that before or same the given date and returns a new ledger.
* @param {Date|string} fromDate * @param {Date|string} fromDate
@@ -139,22 +130,6 @@ export default class Ledger implements ILedger {
return closingBalance; return closingBalance;
} }
/**
* Retrieves the closing credit of the entries.
* @returns {number}
*/
public getClosingCredit(): number {
return sumBy(this.entries, 'credit');
}
/**
* Retrieves the closing debit of the entries.
* @returns {number}
*/
public getClosingDebit(): number {
return sumBy(this.entries, 'debit');
}
/** /**
* Retrieve the closing balance of the entries. * Retrieve the closing balance of the entries.
* @returns {number} * @returns {number}

View File

@@ -1,10 +1,18 @@
import * as R from 'ramda'; import * as R from 'ramda';
import { defaultTo, isEmpty, sumBy } from 'lodash';
import FinancialSheet from '../FinancialSheet'; import FinancialSheet from '../FinancialSheet';
import { import {
IBalanceSheetAggregateNode,
IBalanceSheetAccountNode,
BALANCE_SHEET_SCHEMA_NODE_TYPE,
IBalanceSheetQuery, IBalanceSheetQuery,
INumberFormatQuery, INumberFormatQuery,
IAccount,
IBalanceSheetSchemaNode, IBalanceSheetSchemaNode,
IBalanceSheetSchemaAggregateNode,
IBalanceSheetDataNode, IBalanceSheetDataNode,
IBalanceSheetSchemaAccountNode,
IBalanceSheetCommonNode,
} from '../../../interfaces'; } from '../../../interfaces';
import { BalanceSheetSchema } from './BalanceSheetSchema'; import { BalanceSheetSchema } from './BalanceSheetSchema';
import { BalanceSheetPercentage } from './BalanceSheetPercentage'; import { BalanceSheetPercentage } from './BalanceSheetPercentage';
@@ -16,14 +24,8 @@ import { FinancialSheetStructure } from '../FinancialSheetStructure';
import BalanceSheetRepository from './BalanceSheetRepository'; import BalanceSheetRepository from './BalanceSheetRepository';
import { BalanceSheetQuery } from './BalanceSheetQuery'; import { BalanceSheetQuery } from './BalanceSheetQuery';
import { BalanceSheetFiltering } from './BalanceSheetFiltering'; import { BalanceSheetFiltering } from './BalanceSheetFiltering';
import { BalanceSheetNetIncome } from './BalanceSheetNetIncome';
import { BalanceSheetAggregators } from './BalanceSheetAggregators';
import { BalanceSheetAccounts } from './BalanceSheetAccounts';
export default class BalanceSheet extends R.compose( export default class BalanceSheet extends R.compose(
BalanceSheetAggregators,
BalanceSheetAccounts,
BalanceSheetNetIncome,
BalanceSheetFiltering, BalanceSheetFiltering,
BalanceSheetDatePeriods, BalanceSheetDatePeriods,
BalanceSheetComparsionPreviousPeriod, BalanceSheetComparsionPreviousPeriod,
@@ -51,9 +53,6 @@ export default class BalanceSheet extends R.compose(
*/ */
readonly baseCurrency: string; readonly baseCurrency: string;
/**
* Localization.
*/
readonly i18n: any; readonly i18n: any;
/** /**
@@ -78,18 +77,216 @@ export default class BalanceSheet extends R.compose(
} }
/** /**
* Parses report schema nodes. * Retrieve the accounts node of accounts types.
* @param {IBalanceSheetSchemaNode[]} schema * @param {string} accountsTypes
* @returns {IBalanceSheetDataNode[]} * @returns {IAccount[]}
*/ */
public parseSchemaNodes = ( private getAccountsByAccountTypes = (accountsTypes: string[]): IAccount[] => {
schema: IBalanceSheetSchemaNode[] const mapAccountsByTypes = R.map((accountType) =>
): IBalanceSheetDataNode[] => { defaultTo(this.repository.accountsByType.get(accountType), [])
);
return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes);
};
/**
* Mappes the aggregate schema node type.
* @param {IBalanceSheetSchemaAggregateNode} node - Schema node.
* @return {IBalanceSheetAggregateNode}
*/
private reportSchemaAggregateNodeMapper = (
node: IBalanceSheetSchemaAggregateNode
): IBalanceSheetAggregateNode => {
const total = this.getTotalOfNodes(node.children);
return {
name: this.i18n.__(node.name),
id: node.id,
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
total: this.getTotalAmountMeta(total),
children: node.children,
};
};
/**
* Compose shema aggregate node of balance sheet schema.
* @param {IBalanceSheetSchemaAggregateNode} node
* @returns {IBalanceSheetSchemaAggregateNode}
*/
private schemaAggregateNodeCompose = (
node: IBalanceSheetSchemaAggregateNode
) => {
return R.compose( return R.compose(
this.aggregatesSchemaParser, this.aggregateNodeTotalMapper,
this.netIncomeSchemaParser, this.reportSchemaAggregateNodeMapper
this.accountsSchemaParser )(node);
)(schema) as IBalanceSheetDataNode[]; };
/**
* Mappes the account model to report account node.
* @param {IAccount} account
* @returns {IBalanceSheetAccountNode}
*/
private reportSchemaAccountNodeMapper = (
account: IAccount
): IBalanceSheetAccountNode => {
const total = this.repository.totalAccountsLedger
.whereAccountId(account.id)
.getClosingBalance();
return {
id: account.id,
index: account.index,
name: account.name,
code: account.code,
total: this.getAmountMeta(total),
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT,
};
};
/**
*
* @param {IAccount} account
* @returns {IBalanceSheetAccountNode}
*/
private reportSchemaAccountNodeComposer = (
account: IAccount
): IBalanceSheetAccountNode => {
return R.compose(
R.when(
this.query.isPreviousYearActive,
this.previousYearAccountNodeComposer
),
R.when(
this.query.isPreviousPeriodActive,
this.previousPeriodAccountNodeComposer
),
R.when(
this.query.isDatePeriodsColumnsType,
this.assocAccountNodeDatePeriods
),
this.reportSchemaAccountNodeMapper
)(account);
};
/**
* Retrieve the total of the given nodes.
* @param {IBalanceSheetCommonNode[]} nodes
* @returns {number}
*/
private getTotalOfNodes = (nodes: IBalanceSheetCommonNode[]) => {
return sumBy(nodes, 'total.amount');
};
/**
* Retrieve the report accounts node by the given accounts types.
* @param {string[]} accountsTypes
* @returns {}
*/
private getAccountsNodesByAccountTypes = (accountsTypes: string[]) => {
const accounts = this.getAccountsByAccountTypes(accountsTypes);
return R.compose(R.map(this.reportSchemaAccountNodeComposer))(accounts);
};
/**
* Mappes the accounts schema node type.
* @param {IBalanceSheetSchemaNode} node - Schema node.
* @returns {IBalanceSheetAccountNode}
*/
private reportSchemaAccountsNodeMapper = (
node: IBalanceSheetSchemaAccountNode
): IBalanceSheetAccountNode => {
const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes);
const total = this.getTotalOfNodes(accounts);
return {
id: node.id,
name: this.i18n.__(node.name),
type: node.type,
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
children: accounts,
total: this.getTotalAmountMeta(total),
};
};
/**
* Compose account schema node to report node.
* @param {IBalanceSheetSchemaAccountNode} node
* @returns {IBalanceSheetAccountNode}
*/
private reportSchemaAccountsNodeComposer = (
node: IBalanceSheetSchemaAccountNode
): IBalanceSheetAccountNode => {
return R.compose(
R.when(
this.query.isPreviousYearActive,
this.previousYearAggregateNodeComposer
),
R.when(
this.query.isPreviousPeriodActive,
this.previousPeriodAggregateNodeComposer
),
R.when(
this.query.isDatePeriodsColumnsType,
this.assocAccountsNodeDatePeriods
),
this.reportSchemaAccountsNodeMapper
)(node);
};
/**
* Mappes the given report schema node.
* @param {IBalanceSheetSchemaNode} node - Schema node.
* @return {IBalanceSheetDataNode}
*/
private reportSchemaNodeMapper = (
schemaNode: IBalanceSheetSchemaNode
): IBalanceSheetDataNode => {
return R.compose(
R.when(
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
this.schemaAggregateNodeCompose
),
R.when(
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
this.reportSchemaAccountsNodeComposer
)
)(schemaNode);
};
/**
* Mappes the report schema nodes.
* @param {IBalanceSheetSchemaNode[]} nodes -
* @return {IBalanceSheetStructureSection[]}
*/
private reportSchemaAccountNodesMapper = (
schemaNodes: IBalanceSheetSchemaNode[]
): IBalanceSheetDataNode[] => {
return this.mapNodesDeepReverse(schemaNodes, this.reportSchemaNodeMapper);
};
/**
* Sets total amount that calculated from node children.
* @param {IBalanceSheetSection} node
* @returns {IBalanceSheetDataNode}
*/
private aggregateNodeTotalMapper = (
node: IBalanceSheetDataNode
): IBalanceSheetDataNode => {
return R.compose(
R.when(
this.query.isPreviousYearActive,
this.previousYearAggregateNodeComposer
),
R.when(
this.query.isPreviousPeriodActive,
this.previousPeriodAggregateNodeComposer
),
R.when(
this.query.isDatePeriodsColumnsType,
this.assocAggregateNodeDatePeriods
)
)(node);
}; };
/** /**
@@ -102,7 +299,7 @@ export default class BalanceSheet extends R.compose(
return R.compose( return R.compose(
this.reportFilterPlugin, this.reportFilterPlugin,
this.reportPercentageCompose, this.reportPercentageCompose,
this.parseSchemaNodes this.reportSchemaAccountNodesMapper
)(balanceSheetSchema); )(balanceSheetSchema);
}; };
} }

View File

@@ -1,182 +0,0 @@
import * as R from 'ramda';
import { defaultTo, toArray } from 'lodash';
import { FinancialSheetStructure } from '../FinancialSheetStructure';
import {
BALANCE_SHEET_SCHEMA_NODE_TYPE,
IAccount,
IBalanceSheetAccountNode,
IBalanceSheetAccountsNode,
IBalanceSheetDataNode,
IBalanceSheetSchemaAccountNode,
IBalanceSheetSchemaNode,
INumberFormatQuery,
} from '@/interfaces';
import { BalanceSheetNetIncome } from './BalanceSheetNetIncome';
import { BalanceSheetFiltering } from './BalanceSheetFiltering';
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
import { BalanceSheetSchema } from './BalanceSheetSchema';
import { BalanceSheetBase } from './BalanceSheetBase';
import { BalanceSheetQuery } from './BalanceSheetQuery';
export const BalanceSheetAccounts = (Base: any) =>
class extends R.compose(
BalanceSheetNetIncome,
BalanceSheetFiltering,
BalanceSheetDatePeriods,
BalanceSheetComparsionPreviousPeriod,
BalanceSheetComparsionPreviousYear,
BalanceSheetPercentage,
BalanceSheetSchema,
BalanceSheetBase,
FinancialSheetStructure
)(Base) {
/**
* Balance sheet query.
* @param {BalanceSheetQuery}
*/
readonly query: BalanceSheetQuery;
/**
* Balance sheet number format query.
* @param {INumberFormatQuery}
*/
readonly numberFormat: INumberFormatQuery;
/**
* Base currency of the organization.
* @param {string}
*/
readonly baseCurrency: string;
/**
* Localization.
*/
readonly i18n: any;
/**
* Retrieve the accounts node of accounts types.
* @param {string} accountsTypes
* @returns {IAccount[]}
*/
private getAccountsByAccountTypes = (
accountsTypes: string[]
): IAccount[] => {
const mapAccountsByTypes = R.map((accountType) =>
defaultTo(this.repository.accountsByType.get(accountType), [])
);
return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes);
};
/**
* Mappes the account model to report account node.
* @param {IAccount} account
* @returns {IBalanceSheetAccountNode}
*/
private reportSchemaAccountNodeMapper = (
account: IAccount
): IBalanceSheetAccountNode => {
const total = this.repository.totalAccountsLedger
.whereAccountId(account.id)
.getClosingBalance();
return {
id: account.id,
index: account.index,
name: account.name,
code: account.code,
total: this.getAmountMeta(total),
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT,
};
};
/**
* Mappes the given account model to the balance sheet account node.
* @param {IAccount} account
* @returns {IBalanceSheetAccountNode}
*/
private reportSchemaAccountNodeComposer = (
account: IAccount
): IBalanceSheetAccountNode => {
return R.compose(
R.when(
this.query.isPreviousYearActive,
this.previousYearAccountNodeComposer
),
R.when(
this.query.isPreviousPeriodActive,
this.previousPeriodAccountNodeComposer
),
R.when(
this.query.isDatePeriodsColumnsType,
this.assocAccountNodeDatePeriods
),
this.reportSchemaAccountNodeMapper
)(account);
};
// -----------------------------
// - Accounts Node Praser
// -----------------------------
/**
* Retrieve the report accounts node by the given accounts types.
* @param {string[]} accountsTypes
* @returns {IBalanceSheetAccountNode[]}
*/
private getAccountsNodesByAccountTypes = (
accountsTypes: string[]
): IBalanceSheetAccountNode[] => {
const accounts = this.getAccountsByAccountTypes(accountsTypes);
return R.map(this.reportSchemaAccountNodeComposer, accounts);
};
/**
* Mappes the accounts schema node type.
* @param {IBalanceSheetSchemaNode} node - Schema node.
* @returns {IBalanceSheetAccountNode}
*/
private reportSchemaAccountsNodeMapper = (
node: IBalanceSheetSchemaAccountNode
): IBalanceSheetAccountsNode => {
const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes);
const children = toArray(node?.children);
return {
id: node.id,
name: this.i18n.__(node.name),
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
children: [...accounts, ...children],
total: this.getTotalAmountMeta(0),
};
};
/**
* Mappes the given report schema node.
* @param {IBalanceSheetSchemaNode | IBalanceSheetDataNode} node - Schema node.
* @return {IBalanceSheetSchemaNode | IBalanceSheetDataNode}
*/
private reportAccountSchemaParser = (
node: IBalanceSheetSchemaNode | IBalanceSheetDataNode
): IBalanceSheetSchemaNode | IBalanceSheetDataNode => {
return R.compose(
R.when(
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
this.reportSchemaAccountsNodeMapper
)
)(node);
};
/**
* Parses the report accounts schema nodes.
* @param {IBalanceSheetSchemaNode[]} nodes -
* @return {IBalanceSheetStructureSection[]}
*/
public accountsSchemaParser = (
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]
): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => {
return this.mapNodesDeepReverse(nodes, this.reportAccountSchemaParser);
};
};

View File

@@ -1,142 +0,0 @@
import * as R from 'ramda';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals';
import { FinancialSheetStructure } from '../FinancialSheetStructure';
import {
BALANCE_SHEET_SCHEMA_NODE_TYPE,
IBalanceSheetAggregateNode,
IBalanceSheetDataNode,
IBalanceSheetSchemaAggregateNode,
IBalanceSheetSchemaNode,
INumberFormatQuery,
} from '@/interfaces';
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
import { BalanceSheetSchema } from './BalanceSheetSchema';
import { BalanceSheetBase } from './BalanceSheetBase';
import { BalanceSheetQuery } from './BalanceSheetQuery';
export const BalanceSheetAggregators = (Base: any) =>
class extends R.compose(
BalanceSheetDatePeriods,
BalanceSheetComparsionPreviousPeriod,
BalanceSheetComparsionPreviousYear,
BalanceSheetPercentage,
BalanceSheetSchema,
BalanceSheetBase,
FinancialSheetStructure
)(Base) {
/**
* Balance sheet query.
* @param {BalanceSheetQuery}
*/
readonly query: BalanceSheetQuery;
/**
* Balance sheet number format query.
* @param {INumberFormatQuery}
*/
readonly numberFormat: INumberFormatQuery;
/**
* Base currency of the organization.
* @param {string}
*/
readonly baseCurrency: string;
/**
* Localization.
*/
readonly i18n: any;
/**
* Sets total amount that calculated from node children.
* @param {IBalanceSheetSection} node
* @returns {IBalanceSheetDataNode}
*/
private aggregateNodeTotalMapper = (
node: IBalanceSheetDataNode
): IBalanceSheetDataNode => {
return R.compose(
R.when(
this.query.isPreviousYearActive,
this.previousYearAggregateNodeComposer
),
R.when(
this.query.isPreviousPeriodActive,
this.previousPeriodAggregateNodeComposer
),
R.when(
this.query.isDatePeriodsColumnsType,
this.assocAggregateNodeDatePeriods
)
)(node);
};
/**
* Mappes the aggregate schema node type.
* @param {IBalanceSheetSchemaAggregateNode} node - Schema node.
* @return {IBalanceSheetAggregateNode}
*/
private reportSchemaAggregateNodeMapper = (
node: IBalanceSheetSchemaAggregateNode
): IBalanceSheetAggregateNode => {
const total = this.getTotalOfNodes(node.children);
return {
name: this.i18n.__(node.name),
id: node.id,
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
total: this.getTotalAmountMeta(total),
children: node.children,
};
};
/**
* Compose shema aggregate node of balance sheet schema.
* @param {IBalanceSheetSchemaAggregateNode} node
* @returns {IBalanceSheetSchemaAggregateNode}
*/
private schemaAggregateNodeCompose = (
node: IBalanceSheetSchemaAggregateNode
) => {
return R.compose(
this.aggregateNodeTotalMapper,
this.reportSchemaAggregateNodeMapper
)(node);
};
/**
* Mappes the given report schema node.
* @param {IBalanceSheetSchemaNode} node - Schema node.
* @return {IBalanceSheetDataNode}
*/
private reportAggregateSchemaParser = (
node: IBalanceSheetSchemaNode
): IBalanceSheetDataNode => {
return R.compose(
R.when(
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
this.schemaAggregateNodeCompose
),
R.when(
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
this.schemaAggregateNodeCompose
)
)(node);
};
/**
* Mappes the report schema nodes.
* @param {IBalanceSheetSchemaNode[]} nodes -
* @return {IBalanceSheetStructureSection[]}
*/
public aggregatesSchemaParser = (
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]
): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => {
return this.mapNodesDeepReverse(nodes, this.reportAggregateSchemaParser);
};
};

View File

@@ -6,7 +6,6 @@ import {
IBalanceSheetAggregateNode, IBalanceSheetAggregateNode,
IBalanceSheetTotal, IBalanceSheetTotal,
IBalanceSheetCommonNode, IBalanceSheetCommonNode,
IBalanceSheetComparsions,
} from '@/interfaces'; } from '@/interfaces';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod'; import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals'; import { FinancialHorizTotals } from '../FinancialHorizTotals';

View File

@@ -156,14 +156,13 @@ export const BalanceSheetComparsionPreviousYear = (Base: any) =>
* @param {IBalanceSheetCommonNode} node * @param {IBalanceSheetCommonNode} node
* @returns {IBalanceSheetCommonNode} * @returns {IBalanceSheetCommonNode}
*/ */
public assocPreviousYearAggregateHorizNode = ( private assocPreviousYearAggregateHorizNode = (
node: IBalanceSheetCommonNode node: IBalanceSheetCommonNode
): IBalanceSheetCommonNode => { ) => {
const horizontalTotals = R.addIndex(R.map)( const horizontalTotals = R.addIndex(R.map)(
this.previousYearAggregateHorizNodeComposer(node), this.previousYearAggregateHorizNodeComposer(node),
node.horizontalTotals node.horizontalTotals
) as IBalanceSheetTotal[]; );
return R.assoc('horizontalTotals', horizontalTotals, node); return R.assoc('horizontalTotals', horizontalTotals, node);
}; };
@@ -259,11 +258,12 @@ export const BalanceSheetComparsionPreviousYear = (Base: any) =>
// ------------------------------ // ------------------------------
// # Horizontal Nodes - Aggregate. // # Horizontal Nodes - Aggregate.
// ------------------------------ // ------------------------------
/** /**
* Detarmines whether the given node has horizontal totals. * Detarmines whether the given node has horizontal totals.
* @param {IBalanceSheetCommonNode} node * @param {IBalanceSheetCommonNode} node
* @returns {boolean} * @returns {boolean}
*/ */
public isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) => private isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) =>
!isEmpty(node.horizontalTotals); !isEmpty(node.horizontalTotals);
}; };

View File

@@ -107,9 +107,9 @@ export const BalanceSheetDatePeriods = (Base: FinancialSheet) =>
/** /**
* *
* @param {IBalanceSheetAccountNode} node * @param {IBalanceSheetAccountNode} node
* @param {Date} fromDate * @param {Date} fromDate
* @param {Date} toDate * @param {Date} toDate
* @returns {IBalanceSheetAccountNode} * @returns {IBalanceSheetAccountNode}
*/ */
private getAccountNodeDatePeriod = ( private getAccountNodeDatePeriod = (
@@ -201,7 +201,7 @@ export const BalanceSheetDatePeriods = (Base: FinancialSheet) =>
}; };
/** /**
* *
* @param node * @param node
* @returns * @returns
*/ */

View File

@@ -1,226 +0,0 @@
import * as R from 'ramda';
import {
BALANCE_SHEET_SCHEMA_NODE_TYPE,
IBalanceSheetDataNode,
IBalanceSheetNetIncomeNode,
IBalanceSheetSchemaNetIncomeNode,
IBalanceSheetSchemaNode,
IBalanceSheetTotalPeriod,
} from '@/interfaces';
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals';
import BalanceSheetRepository from './BalanceSheetRepository';
import { BalanceSheetQuery } from './BalanceSheetQuery';
import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP';
import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY';
export const BalanceSheetNetIncome = (Base: any) =>
class extends R.compose(
BalanceSheetNetIncomePP,
BalanceSheetNetIncomePY,
BalanceSheetComparsionPreviousYear,
BalanceSheetComparsionPreviousPeriod,
FinancialPreviousPeriod,
FinancialHorizTotals
)(Base) {
private repository: BalanceSheetRepository;
private query: BalanceSheetQuery;
/**
* Retrieves the closing balance of income accounts.
* @returns {number}
*/
private getIncomeTotal = () => {
const closeingBalance = this.repository.incomeLedger.getClosingBalance();
return closeingBalance;
};
/**
* Retrieves the closing balance of expenses accounts.
* @returns {number}
*/
private getExpensesTotal = () => {
const closingBalance = this.repository.expensesLedger.getClosingBalance();
return closingBalance;
};
/**
* Retrieves the total net income.
* @returns {number}
*/
protected getNetIncomeTotal = () => {
const income = this.getIncomeTotal();
const expenses = this.getExpensesTotal();
return income - expenses;
};
/**
* Mappes the aggregate schema node type.
* @param {IBalanceSheetSchemaNetIncomeNode} node - Schema node.
* @return {IBalanceSheetAggregateNode}
*/
protected schemaNetIncomeNodeMapper = (
node: IBalanceSheetSchemaNetIncomeNode
): IBalanceSheetNetIncomeNode => {
const total = this.getNetIncomeTotal();
return {
id: node.id,
name: this.i18n.__(node.name),
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME,
total: this.getTotalAmountMeta(total),
};
};
/**
* Mapps the net income shcema node to report node.
* @param {IBalanceSheetSchemaNetIncomeNode} node
* @returns {IBalanceSheetNetIncomeNode}
*/
protected schemaNetIncomeNodeCompose = (
node: IBalanceSheetSchemaNetIncomeNode
): IBalanceSheetNetIncomeNode => {
return R.compose(
R.when(
this.query.isPreviousYearActive,
this.previousYearNetIncomeNodeCompose
),
R.when(
this.query.isPreviousPeriodActive,
this.previousPeriodNetIncomeNodeCompose
),
R.when(
this.query.isDatePeriodsColumnsType,
this.assocNetIncomeDatePeriodsNode
),
this.schemaNetIncomeNodeMapper
)(node);
};
// --------------------------------
// # Date Periods
// --------------------------------
/**
* Retreives total income of the given date period.
* @param {number} accountId -
* @param {Date} toDate -
* @returns {number}
*/
private getIncomeDatePeriodTotal = (toDate: Date): number => {
const periodTotalBetween = this.repository.incomePeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const periodOpening =
this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance();
return periodOpening + periodTotalBetween;
};
/**
* Retrieves total expense of the given date period.
* @param {number} accountId -
* @param {Date} toDate -
* @returns {number}
*/
private getExpensesDatePeriodTotal = (toDate: Date): number => {
const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const periodOpening =
this.repository.expensesOpeningAccountLedger.getClosingBalance();
return periodOpening + periodTotalBetween;
};
/**
* Retrieve the given net income date period total.
* @param {number} accountId
* @param {Date} toDate
* @returns {number}
*/
private getNetIncomeDatePeriodTotal = (toDate: Date): number => {
const income = this.getIncomeDatePeriodTotal(toDate);
const expense = this.getExpensesDatePeriodTotal(toDate);
return income - expense;
};
/**
* Retrieves the net income date period node.
* @param {IBalanceSheetNetIncomeNode} node
* @param {Date} fromDate
* @param {Date} toDate
* @returns {IBalanceSheetNetIncomeNode}
*/
private getNetIncomeDatePeriodNode = (
node: IBalanceSheetNetIncomeNode,
fromDate: Date,
toDate: Date
): IBalanceSheetTotalPeriod => {
const periodTotal = this.getNetIncomeDatePeriodTotal(toDate);
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
};
/**
* Retrieve total date periods of the given net income node.
* @param {IBalanceSheetNetIncomeNode} node
* @returns {IBalanceSheetNetIncomeNode}
*/
private getNetIncomeDatePeriodsNode = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetTotalPeriod[] => {
return this.getReportNodeDatePeriods(
node,
this.getNetIncomeDatePeriodNode
);
};
/**
* Assoc total date periods to net income node.
* @param {IBalanceSheetNetIncomeNode} node
* @returns {IBalanceSheetNetIncomeNode}
*/
public assocNetIncomeDatePeriodsNode = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetNetIncomeNode => {
const datePeriods = this.getNetIncomeDatePeriodsNode(node);
return R.assoc('horizontalTotals', datePeriods, node);
};
// -----------------------------
// - Net Income Nodes Praser
// -----------------------------
/**
* Mappes the given report schema node.
* @param {IBalanceSheetSchemaNode} node - Schema node.
* @return {IBalanceSheetDataNode}
*/
private reportNetIncomeNodeSchemaParser = (
schemaNode: IBalanceSheetSchemaNode
): IBalanceSheetDataNode => {
return R.compose(
R.when(
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME),
this.schemaNetIncomeNodeCompose
)
)(schemaNode);
};
/**
* Parses the report net income schema nodes.
* @param {(IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]} nodes -
* @return {IBalanceSheetDataNode[]}
*/
public netIncomeSchemaParser = (
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]
): IBalanceSheetDataNode[] => {
return this.mapNodesDeep(nodes, this.reportNetIncomeNodeSchemaParser);
};
};

View File

@@ -1,120 +0,0 @@
import * as R from 'ramda';
import {
IBalanceSheetNetIncomeNode,
IBalanceSheetTotalPeriod,
} from '@/interfaces';
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals';
import BalanceSheetRepository from './BalanceSheetRepository';
import { BalanceSheetQuery } from './BalanceSheetQuery';
import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP';
import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY';
export const BalanceSheetNetIncomeDatePeriods = (Base: any) =>
class extends R.compose(
BalanceSheetNetIncomePP,
BalanceSheetNetIncomePY,
BalanceSheetComparsionPreviousYear,
BalanceSheetComparsionPreviousPeriod,
FinancialPreviousPeriod,
FinancialHorizTotals
)(Base) {
private repository: BalanceSheetRepository;
private query: BalanceSheetQuery;
// --------------------------------
// # Date Periods
// --------------------------------
/**
* Retreives total income of the given date period.
* @param {number} accountId -
* @param {Date} toDate -
* @returns {number}
*/
private getIncomeDatePeriodTotal = (toDate: Date): number => {
const periodTotalBetween = this.repository.incomePeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const periodOpening =
this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance();
return periodOpening + periodTotalBetween;
};
/**
* Retrieves total expense of the given date period.
* @param {number} accountId -
* @param {Date} toDate -
* @returns {number}
*/
private getExpensesDatePeriodTotal = (toDate: Date): number => {
const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const periodOpening =
this.repository.expensesOpeningAccountLedger.getClosingBalance();
return periodOpening + periodTotalBetween;
};
/**
* Retrieve the given net income date period total.
* @param {number} accountId
* @param {Date} toDate
* @returns {number}
*/
private getNetIncomeDatePeriodTotal = (toDate: Date): number => {
const income = this.getIncomeDatePeriodTotal(toDate);
const expense = this.getExpensesDatePeriodTotal(toDate);
return income - expense;
};
/**
* Retrieves the net income date period node.
* @param {IBalanceSheetNetIncomeNode} node
* @param {Date} fromDate
* @param {Date} toDate
* @returns {IBalanceSheetNetIncomeNode}
*/
private getNetIncomeDatePeriodNode = (
node: IBalanceSheetNetIncomeNode,
fromDate: Date,
toDate: Date
): IBalanceSheetTotalPeriod => {
const periodTotal = this.getNetIncomeDatePeriodTotal(toDate);
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
};
/**
* Retrieve total date periods of the given net income node.
* @param {IBalanceSheetNetIncomeNode} node
* @returns {IBalanceSheetNetIncomeNode}
*/
private getNetIncomeDatePeriodsNode = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetTotalPeriod[] => {
return this.getReportNodeDatePeriods(
node,
this.getNetIncomeDatePeriodNode
);
};
/**
* Assoc total date periods to net income node.
* @param {IBalanceSheetNetIncomeNode} node
* @returns {IBalanceSheetNetIncomeNode}
*/
public assocNetIncomeDatePeriodsNode = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetNetIncomeNode => {
const datePeriods = this.getNetIncomeDatePeriodsNode(node);
return R.assoc('horizontalTotals', datePeriods, node);
};
};

View File

@@ -1,127 +0,0 @@
import * as R from 'ramda';
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals';
import { IBalanceSheetNetIncomeNode, IBalanceSheetTotal } from '@/interfaces';
import { BalanceSheetQuery } from './BalanceSheetQuery';
import BalanceSheetRepository from './BalanceSheetRepository';
export const BalanceSheetNetIncomeDatePeriodsPP = (Base: any) =>
class extends R.compose(
BalanceSheetComparsionPreviousPeriod,
FinancialPreviousPeriod,
FinancialHorizTotals
)(Base) {
query: BalanceSheetQuery;
repository: BalanceSheetRepository;
/**
* Retrieves the PY total income of the given date period.
* @param {number} accountId -
* @param {Date} toDate -
* @return {number}
*/
private getPPIncomeDatePeriodTotal = R.curry((toDate: Date) => {
const PYPeriodsTotal = this.repository.incomePPPeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const PYPeriodsOpeningTotal =
this.repository.incomePPPeriodsOpeningAccountLedger.getClosingBalance();
return PYPeriodsOpeningTotal + PYPeriodsTotal;
});
/**
* Retrieves the PY total expense of the given date period.
* @param {number} accountId -
* @param {Date} toDate -
* @returns {number}
*/
private getPPExpenseDatePeriodTotal = R.curry((toDate: Date) => {
const PYPeriodsTotal = this.repository.expensePPPeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const PYPeriodsOpeningTotal =
this.repository.expensePPPeriodsOpeningAccountLedger.getClosingBalance();
return PYPeriodsOpeningTotal + PYPeriodsTotal;
});
/**
* Retrieve the given net income total of the given period.
* @param {number} accountId - Account id.
* @param {Date} toDate - To date.
* @returns {number}
*/
private getPPNetIncomeDatePeriodTotal = R.curry((toDate: Date) => {
const income = this.getPPIncomeDatePeriodTotal(toDate);
const expense = this.getPPExpenseDatePeriodTotal(toDate);
return income - expense;
});
/**
* Assoc preivous period to account horizontal total node.
* @param {IBalanceSheetAccountNode} node
* @returns {}
*/
private assocPreviousPeriodNetIncomeHorizTotal = R.curry(
(node: IBalanceSheetNetIncomeNode, totalNode) => {
const total = this.getPPNetIncomeDatePeriodTotal(
totalNode.previousPeriodToDate.date
);
return R.assoc('previousPeriod', this.getAmountMeta(total), totalNode);
}
);
/**
* Compose previous period to aggregate horizontal nodes.
* @param {IBalanceSheetTotal} node
* @returns {IBalanceSheetTotal}
*/
private previousPeriodNetIncomeHorizNodeComposer = R.curry(
(
node: IBalanceSheetNetIncomeNode,
horiontalTotalNode: IBalanceSheetTotal
): IBalanceSheetTotal => {
return R.compose(
R.when(
this.query.isPreviousPeriodPercentageActive,
this.assocPreviousPeriodTotalPercentageNode
),
R.when(
this.query.isPreviousPeriodChangeActive,
this.assocPreviousPeriodTotalChangeNode
),
R.when(
this.query.isPreviousPeriodActive,
this.assocPreviousPeriodNetIncomeHorizTotal(node)
),
R.when(
this.query.isPreviousPeriodActive,
this.assocPreviousPeriodHorizNodeFromToDates(
this.query.displayColumnsBy
)
)
)(horiontalTotalNode);
}
);
/**
* Associate the PP to net income horizontal nodes.
* @param {IBalanceSheetCommonNode} node
* @returns {IBalanceSheetCommonNode}
*/
public assocPreviousPeriodNetIncomeHorizNode = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetNetIncomeNode => {
const horizontalTotals = R.addIndex(R.map)(
this.previousPeriodNetIncomeHorizNodeComposer(node),
node.horizontalTotals
) as IBalanceSheetTotal[];
return R.assoc('horizontalTotals', horizontalTotals, node);
};
};

View File

@@ -1,122 +0,0 @@
import * as R from 'ramda';
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals';
import { IBalanceSheetNetIncomeNode, IBalanceSheetTotal } from '@/interfaces';
import { BalanceSheetQuery } from './BalanceSheetQuery';
import BalanceSheetRepository from './BalanceSheetRepository';
export const BalanceSheetNetIncomeDatePeriodsPY = (Base: any) =>
class extends R.compose(
BalanceSheetComparsionPreviousYear,
FinancialPreviousPeriod,
FinancialHorizTotals
)(Base) {
query: BalanceSheetQuery;
repository: BalanceSheetRepository;
/**
* Retrieves the PY total income of the given date period.
* @param {Date} toDate -
* @return {number}
*/
private getPYIncomeDatePeriodTotal = R.curry((toDate: Date) => {
const PYPeriodsTotal = this.repository.incomePYPeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const PYPeriodsOpeningTotal =
this.repository.incomePYPeriodsOpeningAccountLedger.getClosingBalance();
return PYPeriodsOpeningTotal + PYPeriodsTotal;
});
/**
* Retrieves the PY total expense of the given date period.
* @param {Date} toDate -
* @returns {number}
*/
private getPYExpenseDatePeriodTotal = R.curry((toDate: Date) => {
const PYPeriodsTotal = this.repository.expensePYPeriodsAccountsLedger
.whereToDate(toDate)
.getClosingBalance();
const PYPeriodsOpeningTotal =
this.repository.expensePYPeriodsOpeningAccountLedger.getClosingBalance();
return PYPeriodsOpeningTotal + PYPeriodsTotal;
});
/**
* Retrieve the given net income total of the given period.
* @param {Date} toDate - To date.
* @returns {number}
*/
private getPYNetIncomeDatePeriodTotal = R.curry((toDate: Date) => {
const income = this.getPYIncomeDatePeriodTotal(toDate);
const expense = this.getPYExpenseDatePeriodTotal(toDate);
return income - expense;
});
/**
* Assoc preivous year to account horizontal total node.
* @param {IBalanceSheetAccountNode} node
* @returns {}
*/
private assocPreviousYearNetIncomeHorizTotal = R.curry(
(node: IBalanceSheetNetIncomeNode, totalNode) => {
const total = this.getPYNetIncomeDatePeriodTotal(
totalNode.previousYearToDate.date
);
return R.assoc('previousYear', this.getAmountMeta(total), totalNode);
}
);
/**
* Compose PY to net income horizontal nodes.
* @param {IBalanceSheetTotal} node
* @returns {IBalanceSheetTotal}
*/
private previousYearNetIncomeHorizNodeComposer = R.curry(
(
node: IBalanceSheetNetIncomeNode,
horiontalTotalNode: IBalanceSheetTotal
): IBalanceSheetTotal => {
return R.compose(
R.when(
this.query.isPreviousYearPercentageActive,
this.assocPreviousYearTotalPercentageNode
),
R.when(
this.query.isPreviousYearChangeActive,
this.assocPreviousYearTotalChangeNode
),
R.when(
this.query.isPreviousYearActive,
this.assocPreviousYearNetIncomeHorizTotal(node)
),
R.when(
this.query.isPreviousYearActive,
this.assocPreviousYearHorizNodeFromToDates
)
)(horiontalTotalNode);
}
);
/**
* Associate the PY to net income horizontal nodes.
* @param {IBalanceSheetCommonNode} node
* @returns {IBalanceSheetCommonNode}
*/
public assocPreviousYearNetIncomeHorizNode = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetNetIncomeNode => {
const horizontalTotals = R.addIndex(R.map)(
this.previousYearNetIncomeHorizNodeComposer(node),
node.horizontalTotals
) as IBalanceSheetTotal[];
return R.assoc('horizontalTotals', horizontalTotals, node);
};
};

View File

@@ -1,75 +0,0 @@
import * as R from 'ramda';
import {
IBalanceSheetDataNode,
IBalanceSheetNetIncomeNode,
} from '@/interfaces';
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals';
import BalanceSheetRepository from './BalanceSheetRepository';
import { BalanceSheetQuery } from './BalanceSheetQuery';
import { BalanceSheetNetIncomeDatePeriodsPP } from './BalanceSheetNetIncomeDatePeriodsPP';
export const BalanceSheetNetIncomePP = (Base: any) =>
class extends R.compose(
BalanceSheetNetIncomeDatePeriodsPP,
BalanceSheetComparsionPreviousPeriod,
FinancialPreviousPeriod,
FinancialHorizTotals
)(Base) {
private repository: BalanceSheetRepository;
private query: BalanceSheetQuery;
// -------------------------------
// # Previous Period (PP)
// -------------------------------
/**
* Retrieves the PP net income.
* @returns {}
*/
protected getPreviousPeriodNetIncome = () => {
const income = this.repository.incomePPAccountsLedger.getClosingBalance();
const expense =
this.repository.expensePPAccountsLedger.getClosingBalance();
return income - expense;
};
/**
* Associates the previous period to account node.
* @param {IBalanceSheetDataNode} node
* @returns {IBalanceSheetDataNode}
*/
protected assocPreviousPeriodNetIncomeNode = (
node: IBalanceSheetDataNode
): IBalanceSheetDataNode => {
const total = this.getPreviousPeriodNetIncome();
return R.assoc('previousPeriod', this.getAmountMeta(total), node);
};
/**
* Previous period account node composer.
* @param {IBalanceSheetNetIncomeNode} node
* @returns {IBalanceSheetNetIncomeNode}
*/
protected previousPeriodNetIncomeNodeCompose = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetNetIncomeNode => {
return R.compose(
R.when(
this.isNodeHasHorizTotals,
this.assocPreviousPeriodNetIncomeHorizNode
),
R.when(
this.query.isPreviousPeriodPercentageActive,
this.assocPreviousPeriodPercentageNode
),
R.when(
this.query.isPreviousPeriodChangeActive,
this.assocPreviousPeriodChangeNode
),
this.assocPreviousPeriodNetIncomeNode
)(node);
};
};

View File

@@ -1,79 +0,0 @@
import * as R from 'ramda';
import {
IBalanceSheetDataNode,
IBalanceSheetNetIncomeNode,
} from '@/interfaces';
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
import { FinancialHorizTotals } from '../FinancialHorizTotals';
import BalanceSheetRepository from './BalanceSheetRepository';
import { BalanceSheetQuery } from './BalanceSheetQuery';
import { BalanceSheetNetIncomeDatePeriodsPY } from './BalanceSheetNetIncomeDatePeriodsPY';
export const BalanceSheetNetIncomePY = (Base: any) =>
class extends R.compose(
BalanceSheetNetIncomeDatePeriodsPY,
BalanceSheetComparsionPreviousYear,
BalanceSheetComparsionPreviousPeriod,
FinancialPreviousPeriod,
FinancialHorizTotals
)(Base) {
private repository: BalanceSheetRepository;
private query: BalanceSheetQuery;
// ------------------------------
// # Previous Year (PY)
// ------------------------------
/**
* Retrieves the previous year (PY) net income.
* @returns {number}
*/
protected getPreviousYearNetIncome = () => {
const income =
this.repository.incomePYTotalAccountsLedger.getClosingBalance();
const expense =
this.repository.expensePYTotalAccountsLedger.getClosingBalance();
return income - expense;
};
/**
* Assoc previous year on aggregate node.
* @param {IBalanceSheetAccountNode} node
* @returns {IBalanceSheetAccountNode}
*/
protected assocPreviousYearNetIncomeNode = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetNetIncomeNode => {
const total = this.getPreviousYearNetIncome();
return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
};
/**
* Assoc previous year attributes to aggregate node.
* @param {IBalanceSheetAccountNode} node
* @returns {IBalanceSheetAccountNode}
*/
protected previousYearNetIncomeNodeCompose = (
node: IBalanceSheetNetIncomeNode
): IBalanceSheetNetIncomeNode => {
return R.compose(
R.when(
this.query.isPreviousYearPercentageActive,
this.assocPreviousYearTotalPercentageNode
),
R.when(
this.query.isPreviousYearChangeActive,
this.assocPreviousYearTotalChangeNode
),
// Associate the PY to date periods horizontal nodes.
R.when(
this.isNodeHasHorizontalTotals,
this.assocPreviousYearNetIncomeHorizNode
),
this.assocPreviousYearNetIncomeNode
)(node);
};
};

View File

@@ -12,31 +12,26 @@ export class BalanceSheetQuery extends R.compose(FinancialDateRanges)(
* @param {IBalanceSheetQuery} * @param {IBalanceSheetQuery}
*/ */
public readonly query: IBalanceSheetQuery; public readonly query: IBalanceSheetQuery;
/** /**
* Previous year to date. * Previous year to date.
* @param {Date} * @param {Date}
*/ */
public readonly PYToDate: Date; public readonly PYToDate: Date;
/** /**
* Previous year from date. * Previous year from date.
* @param {Date} * @param {Date}
*/ */
public readonly PYFromDate: Date; public readonly PYFromDate: Date;
/** /**
* Previous period to date. * Previous period to date.
* @param {Date} * @param {Date}
*/ */
public readonly PPToDate: Date; public readonly PPToDate: Date;
/** /**
* Previous period from date. * Previous period from date.
* @param {Date} * @param {Date}
*/ */
public readonly PPFromDate: Date; public readonly PPFromDate: Date;
/** /**
* Constructor method * Constructor method
* @param {IBalanceSheetQuery} query * @param {IBalanceSheetQuery} query

View File

@@ -3,7 +3,6 @@ import * as R from 'ramda';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { import {
IAccount,
IAccountTransactionsGroupBy, IAccountTransactionsGroupBy,
IBalanceSheetQuery, IBalanceSheetQuery,
ILedger, ILedger,
@@ -12,12 +11,9 @@ import { transformToMapBy } from 'utils';
import Ledger from '@/services/Accounting/Ledger'; import Ledger from '@/services/Accounting/Ledger';
import { BalanceSheetQuery } from './BalanceSheetQuery'; import { BalanceSheetQuery } from './BalanceSheetQuery';
import { FinancialDatePeriods } from '../FinancialDatePeriods'; import { FinancialDatePeriods } from '../FinancialDatePeriods';
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/data/AccountTypes';
import { BalanceSheetRepositoryNetIncome } from './BalanceSheetRepositoryNetIncome';
@Service() @Service()
export default class BalanceSheetRepository extends R.compose( export default class BalanceSheetRepository extends R.compose(
BalanceSheetRepositoryNetIncome,
FinancialDatePeriods FinancialDatePeriods
)(class {}) { )(class {}) {
/** /**
@@ -69,22 +65,8 @@ export default class BalanceSheetRepository extends R.compose(
*/ */
public readonly PPFromDate: Date; public readonly PPFromDate: Date;
/**
* Total closing accounts ledger.
* @param {Ledger}
*/
public totalAccountsLedger: Ledger; public totalAccountsLedger: Ledger;
/**
* Total income accounts ledger.
*/
public incomeLedger: Ledger;
/**
* Total expense accounts ledger.
*/
public expensesLedger: Ledger;
/** /**
* Transactions group type. * Transactions group type.
* @param {IAccountTransactionsGroupBy} * @param {IAccountTransactionsGroupBy}
@@ -189,8 +171,6 @@ export default class BalanceSheetRepository extends R.compose(
) { ) {
await this.initPeriodsPreviousPeriod(); await this.initPeriodsPreviousPeriod();
} }
//
await this.asyncInitializeNetIncome();
}; };
// ---------------------------- // ----------------------------
@@ -201,7 +181,6 @@ export default class BalanceSheetRepository extends R.compose(
this.accounts = accounts; this.accounts = accounts;
this.accountsByType = transformToMapBy(accounts, 'accountType'); this.accountsByType = transformToMapBy(accounts, 'accountType');
this.accountsByParentType = transformToMapBy(accounts, 'accountParentType');
}; };
// ---------------------------- // ----------------------------
@@ -330,15 +309,14 @@ export default class BalanceSheetRepository extends R.compose(
/** /**
* Closing accounts date periods. * Closing accounts date periods.
* @param {Date} fromDate * @param openingDate
* @param {Date} toDate * @param datePeriodsType
* @param {string} datePeriodsType
* @returns * @returns
*/ */
public accountsDatePeriods = async ( public accountsDatePeriods = async (
fromDate: Date, fromDate: Date,
toDate: Date, toDate: Date,
datePeriodsType: string datePeriodsType
) => { ) => {
const { AccountTransaction } = this.models; const { AccountTransaction } = this.models;
@@ -358,7 +336,6 @@ export default class BalanceSheetRepository extends R.compose(
/** /**
* Retrieve the opening balance transactions of the report. * Retrieve the opening balance transactions of the report.
* @param {Date|string} openingDate -
*/ */
public closingAccountsTotal = async (openingDate: Date | string) => { public closingAccountsTotal = async (openingDate: Date | string) => {
const { AccountTransaction } = this.models; const { AccountTransaction } = this.models;

View File

@@ -1,222 +0,0 @@
import * as R from 'ramda';
import { IAccount, ILedger } from '@/interfaces';
import { FinancialDatePeriods } from '../FinancialDatePeriods';
import { ACCOUNT_PARENT_TYPE } from '@/data/AccountTypes';
export const BalanceSheetRepositoryNetIncome = (Base) =>
class extends R.compose(FinancialDatePeriods)(Base) {
// -----------------------
// # Net Income
// -----------------------
public incomeAccounts: IAccount[];
public incomeAccountsIds: number[];
public expenseAccounts: IAccount[];
public expenseAccountsIds: number[];
public incomePeriodsAccountsLedger: ILedger;
public incomePeriodsOpeningAccountsLedger: ILedger;
public expensesPeriodsAccountsLedger: ILedger;
public expensesOpeningAccountLedger: ILedger;
public incomePPAccountsLedger: ILedger;
public expensePPAccountsLedger: ILedger;
public incomePPPeriodsAccountsLedger: ILedger;
public incomePPPeriodsOpeningAccountLedger: ILedger;
public expensePPPeriodsAccountsLedger: ILedger;
public expensePPPeriodsOpeningAccountLedger: ILedger;
public incomePYTotalAccountsLedger: ILedger;
public expensePYTotalAccountsLedger: ILedger;
public incomePYPeriodsAccountsLedger: ILedger;
public incomePYPeriodsOpeningAccountLedger: ILedger;
public expensePYPeriodsAccountsLedger: ILedger;
public expensePYPeriodsOpeningAccountLedger: ILedger;
/**
* Async initialize.
* @returns {Promise<void>}
*/
public asyncInitializeNetIncome = async () => {
await this.initAccounts();
await this.initAccountsTotalLedger();
// Net Income
this.initIncomeAccounts();
this.initExpenseAccounts();
this.initIncomeTotalLedger();
this.initExpensesTotalLedger();
// Date periods
if (this.query.isDatePeriodsColumnsType()) {
this.initNetIncomeDatePeriods();
}
// Previous Year (PY).
if (this.query.isPreviousYearActive()) {
this.initNetIncomePreviousYear();
}
// Previous Period (PP).
if (this.query.isPreviousPeriodActive()) {
this.initNetIncomePreviousPeriod();
}
// Previous Year (PY) / Date Periods.
if (
this.query.isPreviousYearActive() &&
this.query.isDatePeriodsColumnsType()
) {
this.initNetIncomePeriodsPreviewYear();
}
// Previous Period (PP) / Date Periods.
if (
this.query.isPreviousPeriodActive() &&
this.query.isDatePeriodsColumnsType()
) {
this.initNetIncomePeriodsPreviousPeriod();
}
};
// ----------------------------
// # Net Income
// ----------------------------
/**
* Initialize income accounts.
*/
private initIncomeAccounts = () => {
const incomeAccounts = this.accountsByParentType.get(
ACCOUNT_PARENT_TYPE.INCOME
);
const incomeAccountsIds = incomeAccounts.map((a) => a.id);
this.incomeAccounts = incomeAccounts;
this.incomeAccountsIds = incomeAccountsIds;
};
/**
* Initialize expense accounts.
*/
private initExpenseAccounts = () => {
const expensesAccounts = this.accountsByParentType.get(
ACCOUNT_PARENT_TYPE.EXPENSE
);
const expensesAccountsIds = expensesAccounts.map((a) => a.id);
this.expenseAccounts = expensesAccounts;
this.expenseAccountsIds = expensesAccountsIds;
};
/**
* Initialize the income total ledger.
*/
private initIncomeTotalLedger = (): void => {
// Inject to the repository.
this.incomeLedger = this.totalAccountsLedger.whereAccountsIds(
this.incomeAccountsIds
);
};
/**
* Initialize the expenses total ledger.
*/
private initExpensesTotalLedger = (): void => {
this.expensesLedger = this.totalAccountsLedger.whereAccountsIds(
this.expenseAccountsIds
);
};
// ----------------------------
// # Net Income - Date Periods
// ----------------------------
/**
* Initialize the net income date periods.
*/
public initNetIncomeDatePeriods = () => {
this.incomePeriodsAccountsLedger =
this.periodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
this.incomePeriodsOpeningAccountsLedger =
this.periodsOpeningAccountLedger.whereAccountsIds(
this.incomeAccountsIds
);
this.expensesPeriodsAccountsLedger =
this.periodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
this.expensesOpeningAccountLedger =
this.periodsOpeningAccountLedger.whereAccountsIds(
this.expenseAccountsIds
);
};
// ----------------------------
// # Net Income - Previous Period
// ----------------------------
/**
* Initialize the total net income PP.
*/
public initNetIncomePreviousPeriod = () => {
this.incomePPAccountsLedger = this.PPTotalAccountsLedger.whereAccountsIds(
this.incomeAccountsIds
);
this.expensePPAccountsLedger =
this.PPTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
};
/**
* Initialize the net income periods of previous period.
*/
public initNetIncomePeriodsPreviousPeriod = () => {
this.incomePPPeriodsAccountsLedger =
this.PPPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
this.incomePPPeriodsOpeningAccountLedger =
this.PPPeriodsOpeningAccountLedger.whereAccountsIds(
this.incomeAccountsIds
);
this.expensePPPeriodsAccountsLedger =
this.PPPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
this.expensePPPeriodsOpeningAccountLedger =
this.PPPeriodsOpeningAccountLedger.whereAccountsIds(
this.expenseAccountsIds
);
};
// ----------------------------
// # Net Income - Previous Year
// ----------------------------
/**
* Initialize the net income PY total.
*/
public initNetIncomePreviousYear = () => {
this.incomePYTotalAccountsLedger =
this.PYTotalAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
this.expensePYTotalAccountsLedger =
this.PYTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
};
/**
* Initialize the net income PY periods.
*/
public initNetIncomePeriodsPreviewYear = () => {
this.incomePYPeriodsAccountsLedger =
this.PYPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
this.incomePYPeriodsOpeningAccountLedger =
this.PYPeriodsOpeningAccountLedger.whereAccountsIds(
this.incomeAccountsIds
);
this.expensePYPeriodsAccountsLedger =
this.PYPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
this.expensePYPeriodsOpeningAccountLedger =
this.PYPeriodsOpeningAccountLedger.whereAccountsIds(
this.expenseAccountsIds
);
};
};

View File

@@ -7,11 +7,12 @@ import {
import { ACCOUNT_TYPE } from '@/data/AccountTypes'; import { ACCOUNT_TYPE } from '@/data/AccountTypes';
import { FinancialSchema } from '../FinancialSchema'; import { FinancialSchema } from '../FinancialSchema';
export const BalanceSheetSchema = (Base) => export const BalanceSheetSchema = (Base) =>
class extends R.compose(FinancialSchema)(Base) { class extends R.compose(FinancialSchema)(Base) {
/** /**
* Retrieves the balance sheet schema. * Retrieves the balance sheet schema.
* @returns * @returns
*/ */
getSchema = () => { getSchema = () => {
return getBalanceSheetSchema(); return getBalanceSheetSchema();
@@ -114,13 +115,6 @@ export const getBalanceSheetSchema = () => [
id: BALANCE_SHEET_SCHEMA_NODE_ID.EQUITY, id: BALANCE_SHEET_SCHEMA_NODE_ID.EQUITY,
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS, type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
accountsTypes: [ACCOUNT_TYPE.EQUITY], accountsTypes: [ACCOUNT_TYPE.EQUITY],
children: [
{
name: 'balance_sheet.net_income',
id: BALANCE_SHEET_SCHEMA_NODE_ID.NET_INCOME,
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME,
},
],
}, },
], ],
alwaysShow: true, alwaysShow: true,

View File

@@ -8,10 +8,6 @@ import {
BALANCE_SHEET_SCHEMA_NODE_TYPE, BALANCE_SHEET_SCHEMA_NODE_TYPE,
IBalanceSheetDataNode, IBalanceSheetDataNode,
IBalanceSheetSchemaNode, IBalanceSheetSchemaNode,
IBalanceSheetNetIncomeNode,
IBalanceSheetAccountNode,
IBalanceSheetAccountsNode,
IBalanceSheetAggregateNode,
} from '@/interfaces'; } from '@/interfaces';
import { tableRowMapper } from 'utils'; import { tableRowMapper } from 'utils';
import FinancialSheet from '../FinancialSheet'; import FinancialSheet from '../FinancialSheet';
@@ -112,13 +108,11 @@ export default class BalanceSheetTable extends R.compose(
}; };
/** /**
* Retrieves the table row from the given report aggregate node. *
* @param {IBalanceSheetAggregateNode} node * @param node
* @returns {ITableRow} * @returns {ITableRow}
*/ */
private aggregateNodeTableRowsMapper = ( private aggregateNodeTableRowsMapper = (node): ITableRow => {
node: IBalanceSheetAggregateNode
): ITableRow => {
const columns = this.commonColumnsAccessors(); const columns = this.commonColumnsAccessors();
const meta = { const meta = {
rowTypes: [IROW_TYPE.AGGREGATE], rowTypes: [IROW_TYPE.AGGREGATE],
@@ -128,13 +122,11 @@ export default class BalanceSheetTable extends R.compose(
}; };
/** /**
* Retrieves the table row from the given report accounts node. *
* @param {IBalanceSheetAccountsNode} node * @param node
* @returns {ITableRow} * @returns {ITableRow}
*/ */
private accountsNodeTableRowsMapper = ( private accountsNodeTableRowsMapper = (node): ITableRow => {
node: IBalanceSheetAccountsNode
): ITableRow => {
const columns = this.commonColumnsAccessors(); const columns = this.commonColumnsAccessors();
const meta = { const meta = {
rowTypes: [IROW_TYPE.ACCOUNTS], rowTypes: [IROW_TYPE.ACCOUNTS],
@@ -144,13 +136,11 @@ export default class BalanceSheetTable extends R.compose(
}; };
/** /**
* Retrieves the table row from the given report account node. *
* @param {IBalanceSheetAccountNode} node * @param {} node
* @returns {ITableRow} * @returns {ITableRow}
*/ */
private accountNodeTableRowsMapper = ( private accountNodeTableRowsMapper = (node): ITableRow => {
node: IBalanceSheetAccountNode
): ITableRow => {
const columns = this.commonColumnsAccessors(); const columns = this.commonColumnsAccessors();
const meta = { const meta = {
@@ -160,22 +150,6 @@ export default class BalanceSheetTable extends R.compose(
return tableRowMapper(node, columns, meta); return tableRowMapper(node, columns, meta);
}; };
/**
* Retrieves the table row from the given report net income node.
* @param {IBalanceSheetNetIncomeNode} node
* @returns {ITableRow}
*/
private netIncomeNodeTableRowsMapper = (
node: IBalanceSheetNetIncomeNode
): ITableRow => {
const columns = this.commonColumnsAccessors();
const meta = {
rowTypes: [IROW_TYPE.NET_INCOME],
id: node.id,
};
return tableRowMapper(node, columns, meta);
};
/** /**
* Mappes the given report node to table rows. * Mappes the given report node to table rows.
* @param {IBalanceSheetDataNode} node - * @param {IBalanceSheetDataNode} node -
@@ -195,10 +169,6 @@ export default class BalanceSheetTable extends R.compose(
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT), this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT),
this.accountNodeTableRowsMapper, this.accountNodeTableRowsMapper,
], ],
[
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME),
this.netIncomeNodeTableRowsMapper,
],
])(node); ])(node);
}; };

View File

@@ -9,6 +9,5 @@ export enum IROW_TYPE {
AGGREGATE = 'AGGREGATE', AGGREGATE = 'AGGREGATE',
ACCOUNTS = 'ACCOUNTS', ACCOUNTS = 'ACCOUNTS',
ACCOUNT = 'ACCOUNT', ACCOUNT = 'ACCOUNT',
NET_INCOME = 'NET_INCOME',
TOTAL = 'TOTAL', TOTAL = 'TOTAL',
} }

View File

@@ -10,26 +10,13 @@ import {
} from '@/interfaces'; } from '@/interfaces';
import FinancialSheet from '../FinancialSheet'; import FinancialSheet from '../FinancialSheet';
import { allPassedConditionsPass, flatToNestedArray } from 'utils'; import { allPassedConditionsPass, flatToNestedArray } from 'utils';
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
export default class TrialBalanceSheet extends FinancialSheet { export default class TrialBalanceSheet extends FinancialSheet {
/** tenantId: number;
* Trial balance sheet query. query: ITrialBalanceSheetQuery;
* @param {ITrialBalanceSheetQuery} query accounts: IAccount & { type: IAccountType }[];
*/ journalFinancial: any;
private query: ITrialBalanceSheetQuery; baseCurrency: string;
/**
* Trial balance sheet repository.
* @param {TrialBalanceSheetRepository}
*/
private repository: TrialBalanceSheetRepository;
/**
* Organization base currency.
* @param {string}
*/
private baseCurrency: string;
/** /**
* Constructor method. * Constructor method.
@@ -41,58 +28,20 @@ export default class TrialBalanceSheet extends FinancialSheet {
constructor( constructor(
tenantId: number, tenantId: number,
query: ITrialBalanceSheetQuery, query: ITrialBalanceSheetQuery,
repository: TrialBalanceSheetRepository, accounts: IAccount & { type: IAccountType }[],
journalFinancial: any,
baseCurrency: string baseCurrency: string
) { ) {
super(); super();
this.tenantId = tenantId; this.tenantId = tenantId;
this.query = query; this.query = query;
this.repository = repository; this.accounts = accounts;
this.journalFinancial = journalFinancial;
this.numberFormat = this.query.numberFormat; this.numberFormat = this.query.numberFormat;
this.baseCurrency = baseCurrency; this.baseCurrency = baseCurrency;
} }
/**
* Retrieves the closing credit of the given account.
* @param {number} accountId
* @returns {number}
*/
public getClosingAccountCredit(accountId: number) {
const depsAccountsIds =
this.repository.accountsDepGraph.dependenciesOf(accountId);
return this.repository.totalAccountsLedger
.whereAccountsIds([accountId, ...depsAccountsIds])
.getClosingCredit();
}
/**
* Retrieves the closing debit of the given account.
* @param {number} accountId
* @returns {number}
*/
public getClosingAccountDebit(accountId: number) {
const depsAccountsIds =
this.repository.accountsDepGraph.dependenciesOf(accountId);
return this.repository.totalAccountsLedger
.whereAccountsIds([accountId, ...depsAccountsIds])
.getClosingDebit();
}
/**
* Retrieves the closing total of the given account.
* @param {number} accountId
* @returns {number}
*/
public getClosingAccountTotal(accountId: number) {
const credit = this.getClosingAccountCredit(accountId);
const debit = this.getClosingAccountDebit(accountId);
return debit - credit;
}
/** /**
* Account mapper. * Account mapper.
* @param {IAccount} account * @param {IAccount} account
@@ -101,28 +50,23 @@ export default class TrialBalanceSheet extends FinancialSheet {
private accountTransformer = ( private accountTransformer = (
account: IAccount & { type: IAccountType } account: IAccount & { type: IAccountType }
): ITrialBalanceAccount => { ): ITrialBalanceAccount => {
const debit = this.getClosingAccountDebit(account.id); const trial = this.journalFinancial.getTrialBalanceWithDepands(account.id);
const credit = this.getClosingAccountCredit(account.id);
const balance = this.getClosingAccountTotal(account.id);
return { return {
id: account.id, id: account.id,
parentAccountId: account.parentAccountId, parentAccountId: account.parentAccountId,
name: account.name, name: account.name,
formattedName: account.code
? `${account.name} - ${account.code}`
: `${account.name}`,
code: account.code, code: account.code,
accountNormal: account.accountNormal, accountNormal: account.accountNormal,
credit, credit: trial.credit,
debit, debit: trial.debit,
balance, balance: trial.balance,
currencyCode: this.baseCurrency, currencyCode: this.baseCurrency,
formattedCredit: this.formatNumber(credit), formattedCredit: this.formatNumber(trial.credit),
formattedDebit: this.formatNumber(debit), formattedDebit: this.formatNumber(trial.debit),
formattedBalance: this.formatNumber(balance), formattedBalance: this.formatNumber(trial.balance),
}; };
}; };
@@ -173,7 +117,10 @@ export default class TrialBalanceSheet extends FinancialSheet {
private filterNoneTransactions = ( private filterNoneTransactions = (
accountNode: ITrialBalanceAccount accountNode: ITrialBalanceAccount
): boolean => { ): boolean => {
return false === this.repository.totalAccountsLedger.isEmpty(); const entries = this.journalFinancial.getAccountEntriesWithDepents(
accountNode.id
);
return entries.length > 0;
}; };
/** /**
@@ -253,11 +200,11 @@ export default class TrialBalanceSheet extends FinancialSheet {
*/ */
public reportData(): ITrialBalanceSheetData { public reportData(): ITrialBalanceSheetData {
// Don't return noting if the journal has no transactions. // Don't return noting if the journal has no transactions.
if (this.repository.totalAccountsLedger.isEmpty()) { if (this.journalFinancial.isEmpty()) {
return null; return null;
} }
// Retrieve accounts nodes. // Retrieve accounts nodes.
const accounts = this.accountsSection(this.repository.accounts); const accounts = this.accountsSection(this.accounts);
// Retrieve account node. // Retrieve account node.
const total = this.tatalSection(accounts); const total = this.tatalSection(accounts);

View File

@@ -1,105 +0,0 @@
import { ITrialBalanceSheetQuery } from '@/interfaces';
import Ledger from '@/services/Accounting/Ledger';
import { Knex } from 'knex';
import { isEmpty } from 'lodash';
import { Service } from 'typedi';
@Service()
export class TrialBalanceSheetRepository {
private query: ITrialBalanceSheetQuery;
private models: any;
public accounts: any;
public accountsDepGraph;
/**
* Total closing accounts ledger.
* @param {Ledger}
*/
public totalAccountsLedger: Ledger;
/**
* Constructor method.
* @param {number} tenantId
* @param {IBalanceSheetQuery} query
*/
constructor(models: any, repos: any, query: ITrialBalanceSheetQuery) {
this.query = query;
this.repos = repos;
this.models = models;
}
/**
* Async initialize.
* @returns {Promise<void>}
*/
public asyncInitialize = async () => {
await this.initAccounts();
await this.initAccountsClosingTotalLedger();
};
// ----------------------------
// # Accounts
// ----------------------------
/**
* Initialize accounts.
* @returns {Promise<void>}
*/
public initAccounts = async () => {
const accounts = await this.getAccounts();
const accountsDepGraph =
await this.repos.accountRepository.getDependencyGraph();
this.accountsDepGraph = accountsDepGraph;
this.accounts = accounts;
};
/**
* Initialize all accounts closing total ledger.
* @return {Promise<void>}
*/
public initAccountsClosingTotalLedger = async (): Promise<void> => {
const totalByAccounts = await this.closingAccountsTotal(this.query.toDate);
this.totalAccountsLedger = Ledger.fromTransactions(totalByAccounts);
};
/**
* Retrieve accounts of the report.
* @return {Promise<IAccount[]>}
*/
private getAccounts = () => {
const { Account } = this.models;
return Account.query();
};
/**
* Retrieve the opening balance transactions of the report.
* @param {Date|string} openingDate -
*/
public closingAccountsTotal = async (openingDate: Date | string) => {
const { AccountTransaction } = this.models;
return AccountTransaction.query().onBuild((query) => {
query.sum('credit as credit');
query.sum('debit as debit');
query.groupBy('accountId');
query.select(['accountId']);
query.modify('filterDateRange', null, openingDate);
query.withGraphFetched('account');
this.commonFilterBranchesQuery(query);
});
};
/**
* Common branches filter query.
* @param {Knex.QueryBuilder} query
*/
private commonFilterBranchesQuery = (query: Knex.QueryBuilder) => {
if (!isEmpty(this.query.branchesIds)) {
query.modify('filterByBranches', this.query.branchesIds);
}
};
}

View File

@@ -2,18 +2,12 @@ import { Service, Inject } from 'typedi';
import moment from 'moment'; import moment from 'moment';
import TenancyService from '@/services/Tenancy/TenancyService'; import TenancyService from '@/services/Tenancy/TenancyService';
import Journal from '@/services/Accounting/JournalPoster'; import Journal from '@/services/Accounting/JournalPoster';
import { import { ITrialBalanceSheetMeta, ITrialBalanceSheetQuery, ITrialBalanceStatement } from '@/interfaces';
ITrialBalanceSheetMeta,
ITrialBalanceSheetQuery,
ITrialBalanceStatement,
} from '@/interfaces';
import TrialBalanceSheet from './TrialBalanceSheet'; import TrialBalanceSheet from './TrialBalanceSheet';
import FinancialSheet from '../FinancialSheet'; import FinancialSheet from '../FinancialSheet';
import InventoryService from '@/services/Inventory/Inventory'; import InventoryService from '@/services/Inventory/Inventory';
import { parseBoolean } from 'utils'; import { parseBoolean } from 'utils';
import { Tenant } from '@/system/models'; import { Tenant } from '@/system/models';
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
import { TrialBalanceSheetTable } from './TrialBalanceSheetTable';
@Service() @Service()
export default class TrialBalanceSheetService extends FinancialSheet { export default class TrialBalanceSheetService extends FinancialSheet {
@@ -57,8 +51,9 @@ export default class TrialBalanceSheetService extends FinancialSheet {
reportMetadata(tenantId: number): ITrialBalanceSheetMeta { reportMetadata(tenantId: number): ITrialBalanceSheetMeta {
const settings = this.tenancy.settings(tenantId); const settings = this.tenancy.settings(tenantId);
const isCostComputeRunning = const isCostComputeRunning = this.inventoryService.isItemsCostComputeRunning(
this.inventoryService.isItemsCostComputeRunning(tenantId); tenantId
);
const organizationName = settings.get({ const organizationName = settings.get({
group: 'organization', group: 'organization',
key: 'name', key: 'name',
@@ -77,8 +72,10 @@ export default class TrialBalanceSheetService extends FinancialSheet {
/** /**
* Retrieve trial balance sheet statement. * Retrieve trial balance sheet statement.
* -------------
* @param {number} tenantId * @param {number} tenantId
* @param {IBalanceSheetQuery} query * @param {IBalanceSheetQuery} query
*
* @return {IBalanceSheetStatement} * @return {IBalanceSheetStatement}
*/ */
public async trialBalanceSheet( public async trialBalanceSheet(
@@ -89,27 +86,43 @@ export default class TrialBalanceSheetService extends FinancialSheet {
...this.defaultQuery, ...this.defaultQuery,
...query, ...query,
}; };
const {
accountRepository,
transactionsRepository,
} = this.tenancy.repositories(tenantId);
const tenant = await Tenant.query() const tenant = await Tenant.query()
.findById(tenantId) .findById(tenantId)
.withGraphFetched('metadata'); .withGraphFetched('metadata');
const models = this.tenancy.models(tenantId); this.logger.info('[trial_balance_sheet] trying to calcualte the report.', {
const repos = this.tenancy.repositories(tenantId); tenantId,
filter,
});
// Retrieve all accounts on the storage.
const accounts = await accountRepository.all();
const accountsGraph = await accountRepository.getDependencyGraph();
const trialBalanceSheetRepos = new TrialBalanceSheetRepository( // Retrieve all journal transactions based on the given query.
models, const transactions = await transactionsRepository.journal({
repos, fromDate: query.fromDate,
filter toDate: query.toDate,
sumationCreditDebit: true,
branchesIds: query.branchesIds
});
// Transform transactions array to journal collection.
const transactionsJournal = Journal.fromTransactions(
transactions,
tenantId,
accountsGraph
); );
await trialBalanceSheetRepos.asyncInitialize();
// Trial balance report instance. // Trial balance report instance.
const trialBalanceInstance = new TrialBalanceSheet( const trialBalanceInstance = new TrialBalanceSheet(
tenantId, tenantId,
filter, filter,
trialBalanceSheetRepos, accounts,
tenant.metadata.baseCurrency transactionsJournal,
tenant.metadata.baseCurrency,
); );
// Trial balance sheet data. // Trial balance sheet data.
const trialBalanceSheetData = trialBalanceInstance.reportData(); const trialBalanceSheetData = trialBalanceInstance.reportData();
@@ -120,27 +133,4 @@ export default class TrialBalanceSheetService extends FinancialSheet {
meta: this.reportMetadata(tenantId), meta: this.reportMetadata(tenantId),
}; };
} }
/**
* Retrieves the trial balance sheet table.
* @param {number} tenantId
* @param {ITrialBalanceSheetQuery} query
* @returns {Promise<any>}
*/
public async trialBalanceSheetTable(
tenantId: number,
query: ITrialBalanceSheetQuery
) {
const trialBalance = await this.trialBalanceSheet(tenantId, query);
const table = new TrialBalanceSheetTable(trialBalance.data, query, {});
return {
table: {
columns: table.tableColumns(),
rows: table.tableRows(),
},
meta: trialBalance.meta,
query: trialBalance.query,
};
}
} }

View File

@@ -1,146 +0,0 @@
import * as R from 'ramda';
import FinancialSheet from '../FinancialSheet';
import { FinancialTable } from '../FinancialTable';
import {
IBalanceSheetStatementData,
ITableColumn,
ITableColumnAccessor,
ITableRow,
ITrialBalanceAccount,
ITrialBalanceSheetData,
ITrialBalanceSheetQuery,
ITrialBalanceTotal,
} from '@/interfaces';
import { tableRowMapper } from '@/utils';
import { IROW_TYPE } from '../BalanceSheet/constants';
import { FinancialSheetStructure } from '../FinancialSheetStructure';
export class TrialBalanceSheetTable extends R.compose(
FinancialTable,
FinancialSheetStructure
)(FinancialSheet) {
/**
* @param {ITrialBalanceSheetData}
*/
public data: ITrialBalanceSheetData;
/**
* Balance sheet query.
* @param {ITrialBalanceSheetQuery}
*/
public query: ITrialBalanceSheetQuery;
/**
* Constructor method.
* @param {IBalanceSheetStatementData} reportData -
* @param {ITrialBalanceSheetQuery} query -
*/
constructor(
data: ITrialBalanceSheetData,
query: ITrialBalanceSheetQuery,
i18n: any
) {
super();
this.data = data;
this.query = query;
this.i18n = i18n;
}
/**
* Retrieve the common columns for all report nodes.
* @param {ITableColumnAccessor[]}
*/
private commonColumnsAccessors = (): ITableColumnAccessor[] => {
return [
{ key: 'account', accessor: 'formattedName' },
{ key: 'debit', accessor: 'formattedDebit' },
{ key: 'credit', accessor: 'formattedCredit' },
{ key: 'total', accessor: 'formattedBalance' },
];
};
/**
* Maps the account node to table row.
* @param {ITrialBalanceAccount} node -
* @returns {ITableRow}
*/
private accountNodeTableRowsMapper = (
node: ITrialBalanceAccount
): ITableRow => {
const columns = this.commonColumnsAccessors();
const meta = {
rowTypes: [IROW_TYPE.ACCOUNT],
id: node.id,
};
return tableRowMapper(node, columns, meta);
};
/**
* Maps the total node to table row.
* @param {ITrialBalanceTotal} node -
* @returns {ITableRow}
*/
private totalNodeTableRowsMapper = (node: ITrialBalanceTotal): ITableRow => {
const columns = this.commonColumnsAccessors();
const meta = {
rowTypes: [IROW_TYPE.TOTAL],
id: node.id,
};
return tableRowMapper(node, columns, meta);
};
/**
* Mappes the given report sections to table rows.
* @param {IBalanceSheetDataNode[]} nodes -
* @return {ITableRow}
*/
private accountsToTableRowsMap = (
nodes: ITrialBalanceAccount[]
): ITableRow[] => {
return this.mapNodesDeep(nodes, this.accountNodeTableRowsMapper);
};
/**
* Retrieves the accounts table rows of the given report data.
* @returns {ITableRow[]}
*/
private accountsTableRows = (): ITableRow[] => {
return this.accountsToTableRowsMap(this.data.accounts);
};
/**
* Maps the given total node to table row.
* @returns {ITableRow}
*/
private totalTableRow = (): ITableRow => {
return this.totalNodeTableRowsMapper(this.data.total);
};
/**
* Retrieves the table rows.
* @returns {ITableRow[]}
*/
public tableRows = (): ITableRow[] => {
return R.compose(
R.append(this.totalTableRow()),
R.concat(this.accountsTableRows())
)([]);
};
/**
* Retrrieves the table columns.
* @returns {ITableColumn[]}
*/
public tableColumns = (): ITableColumn[] => {
return R.compose(
this.tableColumnsCellIndexing,
R.concat([
{ key: 'account_name', label: 'Account' },
{ key: 'debit', label: 'Debit' },
{ key: 'credit', label: 'Credit' },
{ key: 'total', label: 'Total' },
])
)([]);
};
}

View File

@@ -1,5 +0,0 @@
export enum IROW_TYPE {
ACCOUNT = 'ACCOUNT',
TOTAL = 'TOTAL',
}

View File

@@ -55,18 +55,6 @@ export class CreateItem {
itemDTO.inventoryAccountId itemDTO.inventoryAccountId
); );
} }
if (itemDTO.purchaseTaxRateId) {
await this.validators.validatePurchaseTaxRateExistance(
tenantId,
itemDTO.purchaseTaxRateId
);
}
if (itemDTO.sellTaxRateId) {
await this.validators.validateSellTaxRateExistance(
tenantId,
itemDTO.sellTaxRateId
);
}
} }
/** /**

View File

@@ -76,20 +76,6 @@ export class EditItem {
itemDTO.inventoryAccountId itemDTO.inventoryAccountId
); );
} }
// Validate the purchase tax rate id existance.
if (itemDTO.purchaseTaxRateId) {
await this.validators.validatePurchaseTaxRateExistance(
tenantId,
itemDTO.purchaseTaxRateId
);
}
// Validate the sell tax rate id existance.
if (itemDTO.sellTaxRateId) {
await this.validators.validateSellTaxRateExistance(
tenantId,
itemDTO.sellTaxRateId
);
}
// Validate inventory account should be modified in inventory item // Validate inventory account should be modified in inventory item
// has inventory transactions. // has inventory transactions.
await this.validators.validateItemInvnetoryAccountModified( await this.validators.validateItemInvnetoryAccountModified(

View File

@@ -27,8 +27,6 @@ export class GetItem {
.withGraphFetched('category') .withGraphFetched('category')
.withGraphFetched('costAccount') .withGraphFetched('costAccount')
.withGraphFetched('itemWarehouses.warehouse') .withGraphFetched('itemWarehouses.warehouse')
.withGraphFetched('sellTaxRate')
.withGraphFetched('purchaseTaxRate')
.throwIfNotFound(); .throwIfNotFound();
return this.transformer.transform(tenantId, item, new ItemTransformer()); return this.transformer.transform(tenantId, item, new ItemTransformer());

View File

@@ -241,40 +241,4 @@ export class ItemsValidators {
throw new ServiceError(ERRORS.ITEM_CANNOT_CHANGE_INVENTORY_TYPE); throw new ServiceError(ERRORS.ITEM_CANNOT_CHANGE_INVENTORY_TYPE);
} }
} }
/**
* Validate the purchase tax rate id existance.
* @param {number} tenantId -
* @param {number} taxRateId -
*/
public async validatePurchaseTaxRateExistance(
tenantId: number,
taxRateId: number
) {
const { TaxRate } = this.tenancy.models(tenantId);
const foundTaxRate = await TaxRate.query().findById(taxRateId);
if (!foundTaxRate) {
throw new ServiceError(ERRORS.PURCHASE_TAX_RATE_NOT_FOUND);
}
}
/**
* Validate the sell tax rate id existance.
* @param {number} tenantId
* @param {number} taxRateId
*/
public async validateSellTaxRateExistance(
tenantId: number,
taxRateId: number
) {
const { TaxRate } = this.tenancy.models(tenantId);
const foundTaxRate = await TaxRate.query().findById(taxRateId);
if (!foundTaxRate) {
throw new ServiceError(ERRORS.SELL_TAX_RATE_NOT_FOUND);
}
}
} }

View File

@@ -22,10 +22,7 @@ export const ERRORS = {
TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS: 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS', TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS: 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
INVENTORY_ACCOUNT_CANNOT_MODIFIED: 'INVENTORY_ACCOUNT_CANNOT_MODIFIED', INVENTORY_ACCOUNT_CANNOT_MODIFIED: 'INVENTORY_ACCOUNT_CANNOT_MODIFIED',
ITEM_HAS_ASSOCIATED_TRANSACTIONS: 'ITEM_HAS_ASSOCIATED_TRANSACTIONS', ITEM_HAS_ASSOCIATED_TRANSACTIONS: 'ITEM_HAS_ASSOCIATED_TRANSACTIONS'
PURCHASE_TAX_RATE_NOT_FOUND: 'PURCHASE_TAX_RATE_NOT_FOUND',
SELL_TAX_RATE_NOT_FOUND: 'SELL_TAX_RATE_NOT_FOUND',
}; };
export const DEFAULT_VIEW_COLUMNS = []; export const DEFAULT_VIEW_COLUMNS = [];

View File

@@ -203,8 +203,8 @@ export class BillPaymentGLEntries {
/** /**
* Retrieves the payment GL payable entry. * Retrieves the payment GL payable entry.
* @param {IBillPayment} billPayment * @param {IBillPayment} billPayment
* @param {number} APAccountId * @param {number} APAccountId
* @returns {ILedgerEntry} * @returns {ILedgerEntry}
*/ */
private getPaymentGLPayableEntry = ( private getPaymentGLPayableEntry = (

View File

@@ -14,7 +14,6 @@ import {
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform'; import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform'; import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
import HasTenancyService from '@/services/Tenancy/TenancyService'; import HasTenancyService from '@/services/Tenancy/TenancyService';
import { ItemEntriesTaxTransactions } from '@/services/TaxRates/ItemEntriesTaxTransactions';
@Service() @Service()
export class BillDTOTransformer { export class BillDTOTransformer {
@@ -24,9 +23,6 @@ export class BillDTOTransformer {
@Inject() @Inject()
private warehouseDTOTransform: WarehouseTransactionDTOTransform; private warehouseDTOTransform: WarehouseTransactionDTOTransform;
@Inject()
private taxDTOTransformer: ItemEntriesTaxTransactions;
@Inject() @Inject()
private tenancy: HasTenancyService; private tenancy: HasTenancyService;
@@ -77,24 +73,14 @@ export class BillDTOTransformer {
const billNumber = billDTO.billNumber || oldBill?.billNumber; const billNumber = billDTO.billNumber || oldBill?.billNumber;
const initialEntries = billDTO.entries.map((entry) => ({ const initialEntries = billDTO.entries.map((entry) => ({
referenceType: 'Bill', reference_type: 'Bill',
isInclusiveTax: billDTO.isInclusiveTax,
...omit(entry, ['amount']), ...omit(entry, ['amount']),
})); }));
const asyncEntries = await composeAsync( const entries = await composeAsync(
// Associate tax rate from tax id to entries.
this.taxDTOTransformer.assocTaxRateFromTaxIdToEntries(tenantId),
// Associate tax rate id from tax code to entries.
this.taxDTOTransformer.assocTaxRateIdFromCodeToEntries(tenantId),
// Sets the default cost account to the bill entries. // Sets the default cost account to the bill entries.
this.setBillEntriesDefaultAccounts(tenantId) this.setBillEntriesDefaultAccounts(tenantId)
)(initialEntries); )(initialEntries);
const entries = R.compose(
// Remove tax code from entries.
R.map(R.omit(['taxCode']))
)(asyncEntries);
const initialDTO = { const initialDTO = {
...formatDateFields(omit(billDTO, ['open', 'entries']), [ ...formatDateFields(omit(billDTO, ['open', 'entries']), [
'billDate', 'billDate',
@@ -114,8 +100,6 @@ export class BillDTOTransformer {
userId: authorizedUser.id, userId: authorizedUser.id,
}; };
return R.compose( return R.compose(
// Associates tax amount withheld to the model.
this.taxDTOTransformer.assocTaxAmountWithheldFromEntries,
this.branchDTOTransform.transformDTO(tenantId), this.branchDTOTransform.transformDTO(tenantId),
this.warehouseDTOTransform.transformDTO(tenantId) this.warehouseDTOTransform.transformDTO(tenantId)
)(initialDTO); )(initialDTO);

View File

@@ -7,7 +7,6 @@ import { AccountNormal, IBill, IItemEntry, ILedgerEntry } from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService'; import HasTenancyService from '@/services/Tenancy/TenancyService';
import Ledger from '@/services/Accounting/Ledger'; import Ledger from '@/services/Accounting/Ledger';
import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
@Service() @Service()
export class BillGLEntries { export class BillGLEntries {
@@ -17,9 +16,6 @@ export class BillGLEntries {
@Inject() @Inject()
private ledgerStorage: LedgerStorageService; private ledgerStorage: LedgerStorageService;
@Inject()
private itemsEntriesService: ItemsEntriesService;
/** /**
* Creates bill GL entries. * Creates bill GL entries.
* @param {number} tenantId - * @param {number} tenantId -
@@ -47,16 +43,8 @@ export class BillGLEntries {
{}, {},
trx trx
); );
// Find or create tax payable account. const billLedger = this.getBillLedger(bill, APAccount.id);
const taxPayableAccount = await accountRepository.findOrCreateTaxPayable(
{},
trx
);
const billLedger = this.getBillLedger(
bill,
APAccount.id,
taxPayableAccount.id
);
// Commit the GL enties on the storage. // Commit the GL enties on the storage.
await this.ledgerStorage.commit(tenantId, billLedger, trx); await this.ledgerStorage.commit(tenantId, billLedger, trx);
}; };
@@ -95,7 +83,7 @@ export class BillGLEntries {
/** /**
* Retrieves the bill common entry. * Retrieves the bill common entry.
* @param {IBill} bill * @param {IBill} bill
* @returns {ILedgerEntry} * @returns {ILedgerEntry}
*/ */
private getBillCommonEntry = (bill: IBill) => { private getBillCommonEntry = (bill: IBill) => {
@@ -131,7 +119,7 @@ export class BillGLEntries {
(bill: IBill, entry: IItemEntry, index: number): ILedgerEntry => { (bill: IBill, entry: IItemEntry, index: number): ILedgerEntry => {
const commonJournalMeta = this.getBillCommonEntry(bill); const commonJournalMeta = this.getBillCommonEntry(bill);
const localAmount = bill.exchangeRate * entry.amountExludingTax; const localAmount = bill.exchangeRate * entry.amount;
const landedCostAmount = sumBy(entry.allocatedCostEntries, 'cost'); const landedCostAmount = sumBy(entry.allocatedCostEntries, 'cost');
return { return {
@@ -185,7 +173,7 @@ export class BillGLEntries {
return { return {
...commonJournalMeta, ...commonJournalMeta,
credit: bill.totalLocal, credit: bill.localAmount,
accountId: payableAccountId, accountId: payableAccountId,
contactId: bill.vendorId, contactId: bill.vendorId,
accountNormal: AccountNormal.CREDIT, accountNormal: AccountNormal.CREDIT,
@@ -194,62 +182,15 @@ export class BillGLEntries {
}; };
}; };
/**
* Retrieves the bill tax GL entry.
* @param {IBill} bill -
* @param {number} taxPayableAccountId -
* @param {IItemEntry} entry -
* @param {number} index -
* @returns {ILedgerEntry}
*/
private getBillTaxEntry = R.curry(
(
bill: IBill,
taxPayableAccountId: number,
entry: IItemEntry,
index: number
): ILedgerEntry => {
const commonJournalMeta = this.getBillCommonEntry(bill);
return {
...commonJournalMeta,
debit: entry.taxAmount,
index,
indexGroup: 30,
accountId: taxPayableAccountId,
accountNormal: AccountNormal.CREDIT,
taxRateId: entry.taxRateId,
taxRate: entry.taxRate,
};
}
);
/**
* Retrieves the bill tax GL entries.
* @param {IBill} bill
* @param {number} taxPayableAccountId
* @returns {ILedgerEntry[]}
*/
private getBillTaxEntries = (bill: IBill, taxPayableAccountId: number) => {
// Retrieves the non-zero tax entries.
const nonZeroTaxEntries = this.itemsEntriesService.getNonZeroEntries(
bill.entries
);
const transformTaxEntry = this.getBillTaxEntry(bill, taxPayableAccountId);
return nonZeroTaxEntries.map(transformTaxEntry);
};
/** /**
* Retrieves the given bill GL entries. * Retrieves the given bill GL entries.
* @param {IBill} bill * @param {IBill} bill
* @param {number} payableAccountId * @param {number} payableAccountId
* @returns {ILedgerEntry[]} * @returns {ILedgerEntry[]}
*/ */
private getBillGLEntries = ( private getBillGLEntries = (
bill: IBill, bill: IBill,
payableAccountId: number, payableAccountId: number
taxPayableAccountId: number
): ILedgerEntry[] => { ): ILedgerEntry[] => {
const payableEntry = this.getBillPayableEntry(payableAccountId, bill); const payableEntry = this.getBillPayableEntry(payableAccountId, bill);
@@ -260,28 +201,18 @@ export class BillGLEntries {
const landedCostEntries = bill.locatedLandedCosts.map( const landedCostEntries = bill.locatedLandedCosts.map(
landedCostTransformer landedCostTransformer
); );
const taxEntries = this.getBillTaxEntries(bill, taxPayableAccountId);
// Allocate cost entries journal entries. // Allocate cost entries journal entries.
return [payableEntry, ...itemsEntries, ...landedCostEntries, ...taxEntries]; return [payableEntry, ...itemsEntries, ...landedCostEntries];
}; };
/** /**
* Retrieves the given bill ledger. * Retrieves the given bill ledger.
* @param {IBill} bill * @param {IBill} bill
* @param {number} payableAccountId * @param {number} payableAccountId
* @returns {Ledger} * @returns {Ledger}
*/ */
private getBillLedger = ( private getBillLedger = (bill: IBill, payableAccountId: number) => {
bill: IBill, const entries = this.getBillGLEntries(bill, payableAccountId);
payableAccountId: number,
taxPayableAccountId: number
) => {
const entries = this.getBillGLEntries(
bill,
payableAccountId,
taxPayableAccountId
);
return new Ledger(entries); return new Ledger(entries);
}; };

View File

@@ -28,8 +28,7 @@ export class GetBill {
.findById(billId) .findById(billId)
.withGraphFetched('vendor') .withGraphFetched('vendor')
.withGraphFetched('entries.item') .withGraphFetched('entries.item')
.withGraphFetched('branch') .withGraphFetched('branch');
.withGraphFetched('taxes.taxRate');
// Validates the bill existance. // Validates the bill existance.
this.validators.validateBillExistance(bill); this.validators.validateBillExistance(bill);

View File

@@ -1,42 +1,27 @@
import { IBill } from '@/interfaces'; import { IBill } from '@/interfaces';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { SaleInvoiceTaxEntryTransformer } from '@/services/Sales/Invoices/SaleInvoiceTaxEntryTransformer';
import { formatNumber } from 'utils'; import { formatNumber } from 'utils';
export class PurchaseInvoiceTransformer extends Transformer { export class PurchaseInvoiceTransformer extends Transformer {
/** /**
* Include these attributes to sale bill object. * Include these attributes to sale invoice object.
* @returns {Array} * @returns {Array}
*/ */
public includeAttributes = (): string[] => { public includeAttributes = (): string[] => {
return [ return [
'formattedBillDate', 'formattedBillDate',
'formattedDueDate', 'formattedDueDate',
'formattedAmount',
'formattedPaymentAmount', 'formattedPaymentAmount',
'formattedBalance', 'formattedBalance',
'formattedDueAmount', 'formattedDueAmount',
'formattedExchangeRate', 'formattedExchangeRate',
'subtotalFormatted',
'subtotalLocalFormatted',
'subtotalExcludingTaxFormatted',
'taxAmountWithheldLocalFormatted',
'totalFormatted',
'totalLocalFormatted',
'taxes',
]; ];
}; };
/** /**
* Excluded attributes. * Retrieve formatted invoice date.
* @returns {string[]} * @param {IBill} invoice
*/
public excludeAttributes = (): string[] => {
return ['amount', 'amountLocal', 'localAmount'];
};
/**
* Retrieve formatted bill date.
* @param {IBill} bill
* @returns {String} * @returns {String}
*/ */
protected formattedBillDate = (bill: IBill): string => { protected formattedBillDate = (bill: IBill): string => {
@@ -44,8 +29,8 @@ export class PurchaseInvoiceTransformer extends Transformer {
}; };
/** /**
* Retrieve formatted bill date. * Retrieve formatted invoice date.
* @param {IBill} bill * @param {IBill} invoice
* @returns {String} * @returns {String}
*/ */
protected formattedDueDate = (bill: IBill): string => { protected formattedDueDate = (bill: IBill): string => {
@@ -54,7 +39,7 @@ export class PurchaseInvoiceTransformer extends Transformer {
/** /**
* Retrieve formatted bill amount. * Retrieve formatted bill amount.
* @param {IBill} bill * @param {IBill} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedAmount = (bill): string => { protected formattedAmount = (bill): string => {
@@ -63,7 +48,7 @@ export class PurchaseInvoiceTransformer extends Transformer {
/** /**
* Retrieve formatted bill amount. * Retrieve formatted bill amount.
* @param {IBill} bill * @param {IBill} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedPaymentAmount = (bill): string => { protected formattedPaymentAmount = (bill): string => {
@@ -74,7 +59,7 @@ export class PurchaseInvoiceTransformer extends Transformer {
/** /**
* Retrieve formatted bill amount. * Retrieve formatted bill amount.
* @param {IBill} bill * @param {IBill} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedDueAmount = (bill): string => { protected formattedDueAmount = (bill): string => {
@@ -92,90 +77,10 @@ export class PurchaseInvoiceTransformer extends Transformer {
/** /**
* Retrieve the formatted exchange rate. * Retrieve the formatted exchange rate.
* @param {IBill} bill * @param {ISaleInvoice} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedExchangeRate = (bill): string => { protected formattedExchangeRate = (invoice): string => {
return formatNumber(bill.exchangeRate, { return formatNumber(invoice.exchangeRate, { money: false });
currencyCode: this.context.organization.baseCurrency,
});
};
/**
* Retrieves the formatted subtotal.
* @param {IBill} bill
* @returns {string}
*/
protected subtotalFormatted = (bill): string => {
return formatNumber(bill.subtotal, {
currencyCode: bill.currencyCode,
});
};
/**
* Retrieves the local subtotal formatted.
* @param {IBill} bill
* @returns {string}
*/
protected subtotalLocalFormatted = (bill): string => {
return formatNumber(bill.subtotalLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};
/**
* Retrieves the formatted subtotal tax excluded.
* @param {IBill} bill
* @returns {string}
*/
protected subtotalExcludingTaxFormatted = (bill): string => {
return formatNumber(bill.subtotalExludingTax, {
currencyCode: bill.currencyCode,
});
};
/**
* Retrieves the local formatted tax amount withheld
* @param {IBill} bill
* @returns {string}
*/
protected taxAmountWithheldLocalFormatted = (bill): string => {
return formatNumber(bill.taxAmountWithheldLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};
/**
* Retrieves the total formatted.
* @param {IBill} bill
* @returns {string}
*/
protected totalFormatted = (bill): string => {
return formatNumber(bill.total, {
currencyCode: bill.currencyCode,
});
};
/**
* Retrieves the local total formatted.
* @param {IBill} bill
* @returns {string}
*/
protected totalLocalFormatted = (bill): string => {
return formatNumber(bill.totalLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};
/**
* Retrieve the taxes lines of bill.
* @param {Bill} bill
*/
protected taxes = (bill) => {
return this.item(bill.taxes, new SaleInvoiceTaxEntryTransformer(), {
subtotal: bill.subtotal,
isInclusiveTax: bill.isInclusiveTax,
currencyCode: bill.currencyCode,
});
}; };
} }

View File

@@ -218,8 +218,7 @@ export class SaleInvoiceGLEntries {
...commonEntry, ...commonEntry,
credit: entry.taxAmount, credit: entry.taxAmount,
accountId: taxPayableAccountId, accountId: taxPayableAccountId,
index: index + 1, index: index + 3,
indexGroup: 30,
accountNormal: AccountNormal.CREDIT, accountNormal: AccountNormal.CREDIT,
taxRateId: entry.taxRateId, taxRateId: entry.taxRateId,
taxRate: entry.taxRate, taxRate: entry.taxRate,

View File

@@ -62,8 +62,8 @@ export class SaleInvoiceTaxEntryTransformer extends Transformer {
const taxRate = this.taxRate(taxEntry); const taxRate = this.taxRate(taxEntry);
return this.options.isInclusiveTax return this.options.isInclusiveTax
? getInclusiveTaxAmount(this.options.subtotal, taxRate) ? getInclusiveTaxAmount(this.options.amount, taxRate)
: getExlusiveTaxAmount(this.options.subtotal, taxRate); : getExlusiveTaxAmount(this.options.amount, taxRate);
}; };
/** /**

View File

@@ -171,7 +171,7 @@ export class SaleInvoiceTransformer extends Transformer {
*/ */
protected taxes = (invoice) => { protected taxes = (invoice) => {
return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer(), { return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer(), {
subtotal: invoice.subtotal, amount: invoice.amount,
isInclusiveTax: invoice.isInclusiveTax, isInclusiveTax: invoice.isInclusiveTax,
currencyCode: invoice.currencyCode, currencyCode: invoice.currencyCode,
}); });

View File

@@ -115,7 +115,6 @@ export class EditTaxRateService {
// Triggers `onTaxRateEdited` event. // Triggers `onTaxRateEdited` event.
await this.eventPublisher.emitAsync(events.taxRates.onEdited, { await this.eventPublisher.emitAsync(events.taxRates.onEdited, {
editTaxRateDTO, editTaxRateDTO,
oldTaxRate,
taxRate, taxRate,
tenantId, tenantId,
trx, trx,

View File

@@ -1,55 +0,0 @@
import { Knex } from 'knex';
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
@Service()
export class SyncItemTaxRateOnEditTaxRate {
@Inject()
private tenancy: HasTenancyService;
/**
* Syncs the new tax rate created to item default sell tax rate.
* @param {number} tenantId
* @param {number} itemId
* @param {number} sellTaxRateId
*/
public updateItemSellTaxRate = async (
tenantId: number,
oldSellTaxRateId: number,
sellTaxRateId: number,
trx?: Knex.Transaction
) => {
const { Item } = this.tenancy.models(tenantId);
// Can't continue if the old and new sell tax rate id are equal.
if (oldSellTaxRateId === sellTaxRateId) return;
await Item.query().where('sellTaxRateId', oldSellTaxRateId).update({
sellTaxRateId,
});
};
/**
* Syncs the new tax rate created to item default purchase tax rate.
* @param {number} tenantId
* @param {number} itemId
* @param {number} purchaseTaxRateId
*/
public updateItemPurchaseTaxRate = async (
tenantId: number,
oldPurchaseTaxRateId: number,
purchaseTaxRateId: number,
trx?: Knex.Transaction
) => {
const { Item } = this.tenancy.models(tenantId);
// Can't continue if the old and new sell tax rate id are equal.
if (oldPurchaseTaxRateId === purchaseTaxRateId) return;
await Item.query(trx)
.where('purchaseTaxRateId', oldPurchaseTaxRateId)
.update({
purchaseTaxRateId,
});
};
}

View File

@@ -1,45 +0,0 @@
import { Inject, Service } from 'typedi';
import { SyncItemTaxRateOnEditTaxRate } from './SyncItemTaxRateOnEditTaxRate';
import events from '@/subscribers/events';
import { ITaxRateEditedPayload } from '@/interfaces';
import { runAfterTransaction } from '../UnitOfWork/TransactionsHooks';
@Service()
export class SyncItemTaxRateOnEditTaxSubscriber {
@Inject()
private syncItemRateOnEdit: SyncItemTaxRateOnEditTaxRate;
/**
* Attaches events with handles.
*/
public attach(bus) {
bus.subscribe(
events.taxRates.onEdited,
this.handleSyncNewTaxRateToItemTaxRate
);
}
/**
* Syncs the new tax rate created to default item tax rates.
* @param {ITaxRateEditedPayload} payload -
*/
private handleSyncNewTaxRateToItemTaxRate = async ({
taxRate,
tenantId,
oldTaxRate,
trx,
}: ITaxRateEditedPayload) => {
runAfterTransaction(trx, async () => {
await this.syncItemRateOnEdit.updateItemPurchaseTaxRate(
tenantId,
oldTaxRate.id,
taxRate.id
);
await this.syncItemRateOnEdit.updateItemSellTaxRate(
tenantId,
oldTaxRate.id,
taxRate.id
);
});
};
}

View File

@@ -1,89 +0,0 @@
import { Inject, Service } from 'typedi';
import { IBillCreatingPayload, IBillEditingPayload } from '@/interfaces';
import events from '@/subscribers/events';
import { CommandTaxRatesValidators } from '../CommandTaxRatesValidators';
@Service()
export class BillTaxRateValidateSubscriber {
@Inject()
private taxRateDTOValidator: CommandTaxRatesValidators;
/**
* Attaches events with handlers.
*/
public attach(bus) {
bus.subscribe(
events.bill.onCreating,
this.validateBillEntriesTaxCodeExistanceOnCreating
);
bus.subscribe(
events.bill.onCreating,
this.validateBillEntriesTaxIdExistanceOnCreating
);
bus.subscribe(
events.bill.onEditing,
this.validateBillEntriesTaxCodeExistanceOnEditing
);
bus.subscribe(
events.bill.onEditing,
this.validateBillEntriesTaxIdExistanceOnEditing
);
return bus;
}
/**
* Validate bill entries tax rate code existance when creating.
* @param {IBillCreatingPayload}
*/
private validateBillEntriesTaxCodeExistanceOnCreating = async ({
billDTO,
tenantId,
}: IBillCreatingPayload) => {
await this.taxRateDTOValidator.validateItemEntriesTaxCode(
tenantId,
billDTO.entries
);
};
/**
* Validate the tax rate id existance when creating.
* @param {IBillCreatingPayload}
*/
private validateBillEntriesTaxIdExistanceOnCreating = async ({
billDTO,
tenantId,
}: IBillCreatingPayload) => {
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
tenantId,
billDTO.entries
);
};
/**
* Validate bill entries tax rate code existance when editing.
* @param {IBillEditingPayload}
*/
private validateBillEntriesTaxCodeExistanceOnEditing = async ({
tenantId,
billDTO,
}: IBillEditingPayload) => {
await this.taxRateDTOValidator.validateItemEntriesTaxCode(
tenantId,
billDTO.entries
);
};
/**
* Validates the bill entries tax rate id existance when editing.
* @param {ISaleInvoiceEditingPayload} payload -
*/
private validateBillEntriesTaxIdExistanceOnEditing = async ({
tenantId,
billDTO,
}: IBillEditingPayload) => {
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
tenantId,
billDTO.entries
);
};
}

View File

@@ -1,87 +0,0 @@
import { Inject, Service } from 'typedi';
import {
IBIllEventDeletedPayload,
IBillCreatedPayload,
IBillEditedPayload,
ISaleInvoiceCreatedPayload,
ISaleInvoiceDeletedPayload,
ISaleInvoiceEditedPayload,
} from '@/interfaces';
import events from '@/subscribers/events';
import { WriteTaxTransactionsItemEntries } from '../WriteTaxTransactionsItemEntries';
@Service()
export class WriteBillTaxTransactionsSubscriber {
@Inject()
private writeTaxTransactions: WriteTaxTransactionsItemEntries;
/**
* Attaches events with handlers.
*/
public attach(bus) {
bus.subscribe(
events.bill.onCreated,
this.writeInvoiceTaxTransactionsOnCreated
);
bus.subscribe(
events.bill.onEdited,
this.rewriteInvoiceTaxTransactionsOnEdited
);
bus.subscribe(
events.bill.onDeleted,
this.removeInvoiceTaxTransactionsOnDeleted
);
return bus;
}
/**
* Writes the bill tax transactions on invoice created.
* @param {ISaleInvoiceCreatingPaylaod}
*/
private writeInvoiceTaxTransactionsOnCreated = async ({
tenantId,
bill,
trx,
}: IBillCreatedPayload) => {
await this.writeTaxTransactions.writeTaxTransactionsFromItemEntries(
tenantId,
bill.entries,
trx
);
};
/**
* Rewrites the bill tax transactions on invoice edited.
* @param {IBillEditedPayload} payload -
*/
private rewriteInvoiceTaxTransactionsOnEdited = async ({
tenantId,
bill,
trx,
}: IBillEditedPayload) => {
await this.writeTaxTransactions.rewriteTaxRateTransactionsFromItemEntries(
tenantId,
bill.entries,
'Bill',
bill.id,
trx
);
};
/**
* Removes the invoice tax transactions on invoice deleted.
* @param {IBIllEventDeletedPayload}
*/
private removeInvoiceTaxTransactionsOnDeleted = async ({
tenantId,
oldBill,
trx,
}: IBIllEventDeletedPayload) => {
await this.writeTaxTransactions.removeTaxTransactionsFromItemEntries(
tenantId,
oldBill.id,
'Bill',
trx
);
};
}

View File

@@ -0,0 +1,87 @@
import { Inject, Service } from 'typedi';
import events from '@/subscribers/events';
import TenancyService from '@/services/Tenancy/TenancyService';
import BillsService from '@/services/Purchases/Bills';
import {
IBillCreatedPayload,
IBillEditedPayload,
IBIllEventDeletedPayload,
} from '@/interfaces';
@Service()
export default class BillWriteGLEntriesSubscriber {
@Inject()
tenancy: TenancyService;
@Inject()
billsService: BillsService;
/**
* Attaches events with handles.
*/
public attach(bus) {
bus.subscribe(
events.bill.onCreated,
this.handlerWriteJournalEntriesOnCreate
);
bus.subscribe(
events.bill.onEdited,
this.handleOverwriteJournalEntriesOnEdit
);
bus.subscribe(events.bill.onDeleted, this.handlerDeleteJournalEntries);
}
/**
* Handles writing journal entries once bill created.
* @param {IBillCreatedPayload} payload -
*/
private handlerWriteJournalEntriesOnCreate = async ({
tenantId,
billId,
bill,
trx,
}: IBillCreatedPayload) => {
// Can't continue if the bill is not opened yet.
if (!bill.openedAt) return null;
await this.billsService.recordJournalTransactions(
tenantId,
billId,
false,
trx
);
};
/**
* Handles the overwriting journal entries once bill edited.
* @param {IBillEditedPayload} payload -
*/
private handleOverwriteJournalEntriesOnEdit = async ({
tenantId,
billId,
bill,
trx,
}: IBillEditedPayload) => {
// Can't continue if the bill is not opened yet.
if (!bill.openedAt) return null;
await this.billsService.recordJournalTransactions(
tenantId,
billId,
true,
trx
);
};
/**
* Handles revert journal entries on bill deleted.
* @param {IBIllEventDeletedPayload} payload -
*/
private handlerDeleteJournalEntries = async ({
tenantId,
billId,
trx,
}: IBIllEventDeletedPayload) => {
await this.billsService.revertJournalEntries(tenantId, billId, trx);
};
}

View File

@@ -1,11 +0,0 @@
exports.up = function (knex) {
return knex.schema.table('tenants_metadata', (table) => {
table.string('tax_number')
});
};
exports.down = function (knex) {
return knex.schema.table('tenants_metadata', (table) => {
table.dropColumn('tax_number');
});
};

View File

@@ -41,10 +41,10 @@ const mapValuesDeepReverse = (nodes, callback, config?) => {
); );
const mappedNode = callback(node, children); const mappedNode = callback(node, children);
if (!mappedNode.children && children) { _.set(clonedNodes, pathString, {
mappedNode.children = children; ...mappedNode,
} ...(!_.isEmpty(children) ? { children } : {}),
_.set(clonedNodes, pathString, mappedNode); });
}); });
return clonedNodes; return clonedNodes;
}; };

View File

@@ -1,26 +1,17 @@
FROM node:18.16.0-alpine as build FROM node:14.15.0 as build
USER root USER root
WORKDIR /app WORKDIR /app
# Copy application dependency manifests to the container image. # Install dependencies
COPY ./package*.json ./ COPY package*.json ./
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml COPY lerna.json ./
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
COPY ./lerna.json ./lerna.json
COPY ./packages/webapp/package*.json ./packages/webapp/
# Install application dependencies COPY ./packages/webapp/package*.json /app/packages/webapp/
RUN apk update
RUN apk add python3 build-base chromium
# Set PYHTON env RUN npm install
ENV PYTHON=/usr/bin/python3 RUN npm run bootstrap
# Install pnpm packages dependencies
RUN npm install -g pnpm
RUN pnpm install
# Build webapp package # Build webapp package
COPY ./packages/webapp /app/packages/webapp COPY ./packages/webapp /app/packages/webapp

View File

@@ -1,14 +1,20 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack');
const dotenv = require('dotenv-webpack');
module.exports = { module.exports = {
webpack: { webpack: {
plugins: [
new dotenv(),
new webpack.DefinePlugin({
'process.env': {
MONOREPO_VERSION: JSON.stringify(require('../../lerna.json').version),
},
}),
],
alias: { alias: {
'@': path.resolve(__dirname, 'src'), '@': path.resolve(__dirname, 'src'),
}, },
configure: {
resolve: {
fallback: { path: require.resolve('path-browserify') },
},
},
}, },
}; };

View File

@@ -117,12 +117,6 @@
"resolutions": { "resolutions": {
"react-error-overlay": "6.0.9" "react-error-overlay": "6.0.9"
}, },
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": { "browserslist": {
"production": [ "production": [
">0.2%", ">0.2%",

View File

@@ -1,3 +1,5 @@
import React from 'react';
import AccountDrawer from '@/containers/Drawers/AccountDrawer'; import AccountDrawer from '@/containers/Drawers/AccountDrawer';
import ManualJournalDrawer from '@/containers/Drawers/ManualJournalDrawer'; import ManualJournalDrawer from '@/containers/Drawers/ManualJournalDrawer';
import ExpenseDrawer from '@/containers/Drawers/ExpenseDrawer'; import ExpenseDrawer from '@/containers/Drawers/ExpenseDrawer';

View File

@@ -26,10 +26,6 @@ const SelectButton = styled(Button)`
position: relative; position: relative;
padding-right: 30px; padding-right: 30px;
&.bp4-small{
padding-right: 24px;
}
&:not(.is-selected):not([class*='bp4-intent-']):not(.bp4-minimal) { &:not(.is-selected):not([class*='bp4-intent-']):not(.bp4-minimal) {
color: #5c7080; color: #5c7080;
} }

View File

@@ -1,62 +0,0 @@
// @ts-nocheck
import * as R from 'ramda';
import intl from 'react-intl-universal';
import { FSelect } from '@/components';
import { DialogsName } from '@/constants/dialogs';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { MenuItem } from '@blueprintjs/core';
// Create new account renderer.
const createNewItemRenderer = (query, active, handleClick) => {
return (
<MenuItem
icon="add"
text={intl.get('list.create', { value: `"${query}"` })}
active={active}
onClick={handleClick}
/>
);
};
// Create new item from the given query string.
const createNewItemFromQuery = (name) => ({ name });
/**
* Tax rates select field binded with Formik form.
* @returns {JSX.Element}
*/
function TaxRatesSelectRoot({
// #withDialogActions
openDialog,
// #ownProps
allowCreate,
...restProps
}) {
// Maybe inject new item props to select component.
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate
? createNewItemFromQuery
: null;
// Handles the create item click.
const handleCreateItemClick = () => {
openDialog(DialogsName.TaxRateForm);
};
return (
<FSelect
valueAccessor={'id'}
labelAccessor={'code'}
textAccessor={'name_formatted'}
popoverProps={{ minimal: true, usePortal: true, inline: false }}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
onCreateItemSelect={handleCreateItemClick}
{...restProps}
/>
);
}
export const TaxRatesSelect = R.compose(withDialogActions)(TaxRatesSelectRoot);

View File

@@ -1,7 +0,0 @@
import { TaxType } from '@/interfaces/TaxRates';
export const InclusiveTaxOptions = [
{ key: TaxType.Inclusive, label: 'Inclusive of Tax' },
{ key: TaxType.Exclusive, label: 'Exclusive of Tax' },
];

View File

@@ -1,4 +1,5 @@
// @ts-nocheck // @ts-nocheck
import React, { useMemo } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';

View File

@@ -34,10 +34,10 @@ export function Sidebar() {
* @returns {React.JSX} * @returns {React.JSX}
*/ */
function SidebarFooterVersion() { function SidebarFooterVersion() {
const { REACT_APP_VERSION } = process.env; const { MONOREPO_VERSION } = process.env;
if (!REACT_APP_VERSION) { if (!MONOREPO_VERSION) {
return null; return null;
} }
return <div class="sidebar__version">v{REACT_APP_VERSION}</div>; return <div class="sidebar__version">v{MONOREPO_VERSION}</div>;
} }

View File

@@ -1,4 +1,5 @@
// @ts-nocheck // @ts-nocheck
import React from 'react';
import { Button, Popover, Menu, Position } from '@blueprintjs/core'; import { Button, Popover, Menu, Position } from '@blueprintjs/core';
import { Icon } from '@/components'; import { Icon } from '@/components';

View File

@@ -30,7 +30,7 @@ export default function BillDetailHeader() {
<CommercialDocTopHeader> <CommercialDocTopHeader>
<DetailsMenu> <DetailsMenu>
<AmountDetailItem label={intl.get('amount')}> <AmountDetailItem label={intl.get('amount')}>
<h3 class="big-number">{bill.total_formatted}</h3> <h3 class="big-number">{bill.formatted_amount}</h3>
</AmountDetailItem> </AmountDetailItem>
<StatusDetailItem> <StatusDetailItem>
<BillDetailsStatus bill={bill} /> <BillDetailsStatus bill={bill} />

View File

@@ -1,8 +1,11 @@
// @ts-nocheck // @ts-nocheck
import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { import {
TotalLineBorderStyle, TotalLineBorderStyle,
TotalLineTextStyle, TotalLineTextStyle,
FormatNumber,
T, T,
TotalLines, TotalLines,
TotalLine, TotalLine,
@@ -20,20 +23,12 @@ export function BillDetailTableFooter() {
<BillTotalLines labelColWidth={'180px'} amountColWidth={'180px'}> <BillTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
<TotalLine <TotalLine
title={<T id={'bill.details.subtotal'} />} title={<T id={'bill.details.subtotal'} />}
value={bill.subtotal_formatted} value={<FormatNumber value={bill.amount} />}
borderStyle={TotalLineBorderStyle.SingleDark} borderStyle={TotalLineBorderStyle.SingleDark}
/> />
{bill.taxes.map((taxRate) => (
<TotalLine
key={taxRate.id}
title={`${taxRate.name} [${taxRate.tax_rate}%]`}
value={taxRate.tax_rate_amount_formatted}
textStyle={TotalLineTextStyle.Regular}
/>
))}
<TotalLine <TotalLine
title={<T id={'bill.details.total'} />} title={<T id={'bill.details.total'} />}
value={bill.total_formatted} value={bill.formatted_amount}
borderStyle={TotalLineBorderStyle.DoubleDark} borderStyle={TotalLineBorderStyle.DoubleDark}
textStyle={TotalLineTextStyle.Bold} textStyle={TotalLineTextStyle.Bold}
/> />
@@ -44,7 +39,6 @@ export function BillDetailTableFooter() {
<TotalLine <TotalLine
title={<T id={'bill.details.due_amount'} />} title={<T id={'bill.details.due_amount'} />}
value={bill.formatted_due_amount} value={bill.formatted_due_amount}
textStyle={TotalLineTextStyle.Bold}
/> />
</BillTotalLines> </BillTotalLines>
</BillDetailsFooterRoot> </BillDetailsFooterRoot>

View File

@@ -67,14 +67,6 @@ export default function ItemDetailHeader() {
label={intl.get('cost_account_id')} label={intl.get('cost_account_id')}
children={defaultTo(item.cost_account?.name, '-')} children={defaultTo(item.cost_account?.name, '-')}
/> />
<DetailItem
label={intl.get('item.details.sell_tax_rate')}
children={item?.sell_tax_rate?.name}
/>
<DetailItem
label={intl.get('item.details.purchase_tax_rate')}
children={item?.purchase_tax_rate?.name}
/>
<If condition={item.type === 'inventory'}> <If condition={item.type === 'inventory'}>
<DetailItem <DetailItem
label={intl.get('inventory_account')} label={intl.get('inventory_account')}

View File

@@ -1,16 +0,0 @@
import { Box } from '@/components';
import styled from 'styled-components';
export const EntriesActionsBar = styled(Box)`
padding-bottom: 12px;
display: flex;
.bp4-form-group {
margin-bottom: 0;
label.bp4-label {
opacity: 0.6;
margin-right: 8px;
}
}
`;

View File

@@ -8,30 +8,20 @@ import { DataTableEditable } from '@/components';
import { useEditableItemsEntriesColumns } from './components'; import { useEditableItemsEntriesColumns } from './components';
import { import {
useFetchItemRow, useFetchItemRow,
composeRowsOnNewRow,
useComposeRowsOnEditTableCell, useComposeRowsOnEditTableCell,
useComposeRowsOnRemoveTableRow, useComposeRowsOnRemoveTableRow,
useComposeRowsOnNewRow,
} from './utils'; } from './utils';
import { import {
ItemEntriesTableProvider, ItemEntriesTableProvider,
useItemEntriesTableContext, useItemEntriesTableContext,
} from './ItemEntriesTableProvider'; } from './ItemEntriesTableProvider';
import { useUncontrolled } from '@/hooks/useUncontrolled'; import { useUncontrolled } from '@/hooks/useUncontrolled';
import { ItemEntry } from '@/interfaces/ItemEntries';
interface ItemsEntriesTableProps {
initialValue?: ItemEntry;
value?: ItemEntry[];
onChange?: (entries: ItemEntry[]) => void;
taxRates?: any[];
minLinesNumber?: number;
enableTaxRates?: boolean;
}
/** /**
* Items entries table. * Items entries table.
*/ */
function ItemsEntriesTable(props: ItemsEntriesTableProps) { function ItemsEntriesTable(props) {
const { value, initialValue, onChange } = props; const { value, initialValue, onChange } = props;
const [localValue, handleChange] = useUncontrolled({ const [localValue, handleChange] = useUncontrolled({
@@ -61,7 +51,6 @@ function ItemEntriesTableRoot() {
currencyCode, currencyCode,
landedCost, landedCost,
taxRates, taxRates,
itemType,
} = useItemEntriesTableContext(); } = useItemEntriesTableContext();
// Editiable items entries columns. // Editiable items entries columns.
@@ -69,12 +58,11 @@ function ItemEntriesTableRoot() {
const composeRowsOnEditCell = useComposeRowsOnEditTableCell(); const composeRowsOnEditCell = useComposeRowsOnEditTableCell();
const composeRowsOnDeleteRow = useComposeRowsOnRemoveTableRow(); const composeRowsOnDeleteRow = useComposeRowsOnRemoveTableRow();
const composeRowsOnNewRow = useComposeRowsOnNewRow();
// Handle the fetch item row details. // Handle the fetch item row details.
const { setItemRow, cellsLoading, isItemFetching } = useFetchItemRow({ const { setItemRow, cellsLoading, isItemFetching } = useFetchItemRow({
landedCost, landedCost,
itemType, itemType: null,
notifyNewRow: (newRow, rowIndex) => { notifyNewRow: (newRow, rowIndex) => {
// Update the rate, description and quantity data of the row. // Update the rate, description and quantity data of the row.
const newRows = composeRowsOnNewRow(rowIndex, newRow, localValue); const newRows = composeRowsOnNewRow(rowIndex, newRow, localValue);
@@ -131,11 +119,8 @@ ItemsEntriesTable.defaultProps = {
discount: '', discount: '',
}, },
initialEntries: [], initialEntries: [],
taxRates: [],
items: [],
linesNumber: 1, linesNumber: 1,
minLinesNumber: 1, minLinesNumber: 1,
enableTaxRates: true,
}; };
export default ItemsEntriesTable; export default ItemsEntriesTable;

View File

@@ -92,7 +92,7 @@ const LandedCostHeaderCell = () => {
*/ */
export function useEditableItemsEntriesColumns() { export function useEditableItemsEntriesColumns() {
const { featureCan } = useFeatureCan(); const { featureCan } = useFeatureCan();
const { landedCost, enableTaxRates } = useItemEntriesTableContext(); const { landedCost } = useItemEntriesTableContext();
const isProjectsFeatureEnabled = featureCan(Features.Projects); const isProjectsFeatureEnabled = featureCan(Features.Projects);
@@ -132,17 +132,13 @@ export function useEditableItemsEntriesColumns() {
width: 70, width: 70,
align: Align.Right, align: Align.Right,
}, },
...(enableTaxRates {
? [ Header: 'Tax rate',
{ accessor: 'tax_rate_id',
Header: 'Tax rate', Cell: TaxRatesSuggestInputCell,
accessor: 'tax_rate_id', disableSortBy: true,
Cell: TaxRatesSuggestInputCell, width: 110,
disableSortBy: true, },
width: 110,
},
]
: []),
{ {
Header: intl.get('discount'), Header: intl.get('discount'),
accessor: 'discount', accessor: 'discount',

View File

@@ -1,7 +1,8 @@
// @ts-nocheck // @ts-nocheck
import React, { useCallback, useMemo } from 'react'; import React, { useCallback } from 'react';
import * as R from 'ramda'; import * as R from 'ramda';
import { sumBy, isEmpty, last, keyBy, groupBy } from 'lodash'; import { sumBy, isEmpty, last, keyBy } from 'lodash';
import { useItem } from '@/hooks/query'; import { useItem } from '@/hooks/query';
import { import {
toSafeNumber, toSafeNumber,
@@ -11,7 +12,6 @@ import {
updateAutoAddNewLine, updateAutoAddNewLine,
orderingLinesIndexes, orderingLinesIndexes,
updateTableRow, updateTableRow,
formattedAmount,
} from '@/utils'; } from '@/utils';
import { useItemEntriesTableContext } from './ItemEntriesTableProvider'; import { useItemEntriesTableContext } from './ItemEntriesTableProvider';
@@ -119,24 +119,17 @@ export function useFetchItemRow({ landedCost, itemType, notifyNewRow }) {
// Detarmines whether the landed cost checkbox should be disabled. // Detarmines whether the landed cost checkbox should be disabled.
const landedCostDisabled = isLandedCostDisabled(item); const landedCostDisabled = isLandedCostDisabled(item);
const taxRateId =
itemType === ITEM_TYPE.PURCHASABLE
? item.purchase_tax_rate_id
: item.sell_tax_rate_id;
// The new row. // The new row.
const newRow = { const newRow = {
rate: price, rate: price,
description, description,
quantity: 1, quantity: 1,
tax_rate_id: taxRateId,
...(landedCost ...(landedCost
? { ? {
landed_cost: false, landed_cost: false,
landed_cost_disabled: landedCostDisabled, landed_cost_disabled: landedCostDisabled,
} }
: {}), : {}),
taxRateId,
}; };
setItemRow(null); setItemRow(null);
saveInvoke(notifyNewRow, newRow, rowIndex); saveInvoke(notifyNewRow, newRow, rowIndex);
@@ -165,21 +158,13 @@ export const composeRowsOnEditCell = R.curry(
/** /**
* Compose table rows when insert a new row to table rows. * Compose table rows when insert a new row to table rows.
*/ */
export const useComposeRowsOnNewRow = () => { export const composeRowsOnNewRow = R.curry((rowIndex, newRow, rows) => {
const { taxRates, isInclusiveTax } = useItemEntriesTableContext(); return compose(
orderingLinesIndexes,
return React.useMemo(() => { updateItemsEntriesTotal,
return R.curry((rowIndex, newRow, rows) => { updateTableRow(rowIndex, newRow),
return compose( )(rows);
assignEntriesTaxAmount(isInclusiveTax), });
assignEntriesTaxRate(taxRates),
orderingLinesIndexes,
updateItemsEntriesTotal,
updateTableRow(rowIndex, newRow),
)(rows);
});
}, [isInclusiveTax, taxRates]);
};
/** /**
* Associate tax rate to entries. * Associate tax rate to entries.
@@ -281,29 +266,3 @@ export const useComposeRowsOnRemoveTableRow = () => {
[minLinesNumber, defaultEntry, localValue], [minLinesNumber, defaultEntry, localValue],
); );
}; };
/**
* Retrieves the aggregate tax rates from the given item entries.
*/
export const aggregateItemEntriesTaxRates = R.curry((taxRates, entries) => {
const taxRatesById = keyBy(taxRates, 'id');
// Calculate the total tax amount of invoice entries.
const filteredEntries = entries.filter((e) => e.tax_rate_id);
const groupedTaxRates = groupBy(filteredEntries, 'tax_rate_id');
return Object.keys(groupedTaxRates).map((taxRateId) => {
const taxRate = taxRatesById[taxRateId];
const taxRates = groupedTaxRates[taxRateId];
const totalTaxAmount = sumBy(taxRates, 'tax_amount');
const taxAmountFormatted = formattedAmount(totalTaxAmount, 'USD');
return {
taxRateId,
taxRate: taxRate.rate,
label: `${taxRate.name} [${taxRate.rate}%]`,
taxAmount: totalTaxAmount,
taxAmountFormatted,
};
});
});

View File

@@ -62,7 +62,6 @@ const BalanceSheetDataTable = styled(ReportDataTable)`
border-bottom: 0; border-bottom: 0;
padding-top: 0.32rem; padding-top: 0.32rem;
padding-bottom: 0.32rem; padding-bottom: 0.32rem;
color: #252A31;
} }
&.is-expanded { &.is-expanded {
.td:not(.name) .cell-inner { .td:not(.name) .cell-inner {
@@ -73,7 +72,6 @@ const BalanceSheetDataTable = styled(ReportDataTable)`
.td { .td {
font-weight: 500; font-weight: 500;
border-top: 1px solid #bbb; border-top: 1px solid #bbb;
color: #000;
} }
} }

View File

@@ -60,8 +60,9 @@ const CashflowStatementDataTable = styled(DataTable)`
border-bottom: 0; border-bottom: 0;
padding-top: 0.32rem; padding-top: 0.32rem;
padding-bottom: 0.32rem; padding-bottom: 0.32rem;
color: #252a31;
} }
// &.row-type--AGGREGATE,
&.row_type--ACCOUNTS { &.row_type--ACCOUNTS {
border-top: 1px solid #bbb; border-top: 1px solid #bbb;
} }
@@ -71,9 +72,6 @@ const CashflowStatementDataTable = styled(DataTable)`
&.row_type--TOTAL { &.row_type--TOTAL {
font-weight: 500; font-weight: 500;
.td {
color: #000;
}
&:not(:first-child) .td { &:not(:first-child) .td {
border-top: 1px solid #bbb; border-top: 1px solid #bbb;
} }

View File

@@ -61,7 +61,6 @@ const ProfitLossDataTable = styled(ReportDataTable)`
border-bottom: 0; border-bottom: 0;
padding-top: 0.32rem; padding-top: 0.32rem;
padding-bottom: 0.32rem; padding-bottom: 0.32rem;
color: #252A31;
} }
&.is-expanded { &.is-expanded {
.td:not(.name) .cell-inner { .td:not(.name) .cell-inner {
@@ -72,7 +71,6 @@ const ProfitLossDataTable = styled(ReportDataTable)`
.td { .td {
font-weight: 500; font-weight: 500;
border-top: 1px solid #bbb; border-top: 1px solid #bbb;
color: #000;
} }
} }
&:last-of-type .td { &:last-of-type .td {

View File

@@ -15,6 +15,7 @@ import {
} from './components'; } from './components';
import withTrialBalanceActions from './withTrialBalanceActions'; import withTrialBalanceActions from './withTrialBalanceActions';
import { compose } from '@/utils'; import { compose } from '@/utils';
/** /**

View File

@@ -8,7 +8,8 @@ import { tableRowTypesToClassnames } from '@/utils';
import { ReportDataTable, FinancialSheet } from '@/components'; import { ReportDataTable, FinancialSheet } from '@/components';
import { useTrialBalanceSheetContext } from './TrialBalanceProvider'; import { useTrialBalanceSheetContext } from './TrialBalanceProvider';
import { useTrialBalanceSheetTableColumns } from './hooks'; import { useTrialBalanceTableColumns } from './components';
/** /**
* Trial Balance sheet data table. * Trial Balance sheet data table.
@@ -16,12 +17,12 @@ import { useTrialBalanceSheetTableColumns } from './hooks';
export default function TrialBalanceSheetTable({ companyName }) { export default function TrialBalanceSheetTable({ companyName }) {
// Trial balance sheet context. // Trial balance sheet context.
const { const {
trialBalanceSheet: { table, query }, trialBalanceSheet: { tableRows, query },
isLoading, isLoading,
} = useTrialBalanceSheetContext(); } = useTrialBalanceSheetContext();
// Trial balance sheet table columns. // Trial balance sheet table columns.
const columns = useTrialBalanceSheetTableColumns(); const columns = useTrialBalanceTableColumns();
return ( return (
<FinancialSheet <FinancialSheet
@@ -35,7 +36,7 @@ export default function TrialBalanceSheetTable({ companyName }) {
> >
<TrialBalanceDataTable <TrialBalanceDataTable
columns={columns} columns={columns}
data={table.rows} data={tableRows}
expandable={true} expandable={true}
expandToggleColumn={1} expandToggleColumn={1}
expandColumnSpace={1} expandColumnSpace={1}
@@ -58,7 +59,7 @@ const TrialBalanceDataTable = styled(ReportDataTable)`
.balance.td { .balance.td {
border-top-color: #000; border-top-color: #000;
} }
.tr.row_type--TOTAL .td { .tr.row_type--total .td {
border-top: 1px solid #bbb; border-top: 1px solid #bbb;
font-weight: 500; font-weight: 500;
border-bottom: 3px double #000; border-bottom: 3px double #000;

View File

@@ -1,10 +1,88 @@
// @ts-nocheck // @ts-nocheck
import React from 'react';
import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import { Align } from '@/constants';
import { getColumnWidth } from '@/utils';
import { CellTextSpan } from '@/components/Datatable/Cells';
import { If, Icon, FormattedMessage as T } from '@/components'; import { If, Icon, FormattedMessage as T } from '@/components';
import { useTrialBalanceSheetContext } from './TrialBalanceProvider'; import { useTrialBalanceSheetContext } from './TrialBalanceProvider';
import { FinancialComputeAlert } from '../FinancialReportPage'; import { FinancialComputeAlert } from '../FinancialReportPage';
import FinancialLoadingBar from '../FinancialLoadingBar'; import FinancialLoadingBar from '../FinancialLoadingBar';
/**
* Retrieves the credit column.
*/
const getCreditColumn = (data) => {
const width = getColumnWidth(data, `credit`, { minWidth: 140 });
return {
Header: intl.get('credit'),
Cell: CellTextSpan,
accessor: 'formatted_credit',
className: 'credit',
width,
textOverview: true,
align: Align.Right,
};
};
/**
* Retrieves the debit column.
*/
const getDebitColumn = (data) => {
return {
Header: intl.get('debit'),
Cell: CellTextSpan,
accessor: 'formatted_debit',
width: getColumnWidth(data, `debit`, { minWidth: 140 }),
textOverview: true,
align: Align.Right,
};
};
/**
* Retrieves the balance column.
*/
const getBalanceColumn = (data) => {
return {
Header: intl.get('balance'),
Cell: CellTextSpan,
accessor: 'formatted_balance',
className: 'balance',
width: getColumnWidth(data, `balance`, { minWidth: 140 }),
textOverview: true,
align: Align.Right,
};
};
/**
* Retrieve trial balance sheet table columns.
*/
export const useTrialBalanceTableColumns = () => {
// Trial balance sheet context.
const {
trialBalanceSheet: { tableRows },
} = useTrialBalanceSheetContext();
return React.useMemo(
() => [
{
Header: intl.get('account_name'),
accessor: (row) => (row.code ? `${row.name} - ${row.code}` : row.name),
className: 'name',
width: 350,
textOverview: true,
},
getCreditColumn(tableRows),
getDebitColumn(tableRows),
getBalanceColumn(tableRows),
],
[tableRows],
);
};
/** /**
* Trial balance sheet progress loading bar. * Trial balance sheet progress loading bar.
*/ */
@@ -23,7 +101,7 @@ export function TrialBalanceSheetLoadingBar() {
*/ */
export function TrialBalanceSheetAlerts() { export function TrialBalanceSheetAlerts() {
const { const {
trialBalanceSheet, trialBalanceSheet: { meta },
isLoading, isLoading,
refetchSheet, refetchSheet,
} = useTrialBalanceSheetContext(); } = useTrialBalanceSheetContext();
@@ -37,7 +115,7 @@ export function TrialBalanceSheetAlerts() {
return null; return null;
} }
// Can't continue if the cost compute job is not running. // Can't continue if the cost compute job is not running.
if (!trialBalanceSheet?.meta.is_cost_compute_running) { if (!meta.is_cost_compute_running) {
return null; return null;
} }

View File

@@ -1,56 +0,0 @@
// @ts-nocheck
import * as R from 'ramda';
import { Align } from '@/constants';
import { getColumnWidth } from '@/utils';
const ACCOUNT_NAME_COLUMN_WIDTH = 320;
const AMOUNT_COLUMNS_MIN_WIDTH = 120;
const AMOUNT_COLUMNS_MAGIC_SPACING = 10;
const getTableCellValueAccessor = (index: number) => `cells[${index}].value`;
const accountNameAccessor = R.curry((data, column) => {
const accessor = getTableCellValueAccessor(column.cell_index);
return {
Header: column.label,
id: column.key,
accessor,
className: column.key,
width: ACCOUNT_NAME_COLUMN_WIDTH,
};
});
const amountAccessor = R.curry((data, column) => {
const accessor = getTableCellValueAccessor(column.cell_index);
return {
Header: column.label,
id: column.key,
accessor,
className: column.key,
width: getColumnWidth(data, accessor, {
magicSpacing: AMOUNT_COLUMNS_MAGIC_SPACING,
minWidth: AMOUNT_COLUMNS_MIN_WIDTH,
}),
align: Align.Right,
};
});
const dynamicColumnMapper = R.curry((data, column) => {
const accountNameColumn = accountNameAccessor(data);
const creditColumn = amountAccessor(data);
const debitColumn = amountAccessor(data);
const totalColumn = amountAccessor(data);
return R.compose(
R.when(R.pathEq(['key'], 'account_name'), accountNameColumn),
R.when(R.pathEq(['key'], 'credit'), creditColumn),
R.when(R.pathEq(['key'], 'debit'), debitColumn),
R.when(R.pathEq(['key'], 'total'), totalColumn),
)(column);
});
export const trialBalancesheetDynamicColumns = (columns, data) => {
return R.map(dynamicColumnMapper(data), columns);
};

View File

@@ -1,18 +0,0 @@
// @ts-nocheck
import React from 'react';
import { useTrialBalanceSheetContext } from './TrialBalanceProvider';
import { trialBalancesheetDynamicColumns } from './dynamicColumns';
/**
* Retrieves the trial balance sheet columns.
*/
export const useTrialBalanceSheetTableColumns = () => {
const {
trialBalanceSheet: { table },
} = useTrialBalanceSheetContext();
return React.useMemo(
() => trialBalancesheetDynamicColumns(table.columns, table.rows),
[table],
);
};

View File

@@ -29,16 +29,14 @@ import {
costPriceFieldShouldUpdate, costPriceFieldShouldUpdate,
costAccountFieldShouldUpdate, costAccountFieldShouldUpdate,
purchaseDescFieldShouldUpdate, purchaseDescFieldShouldUpdate,
taxRateFieldShouldUpdate,
} from './utils'; } from './utils';
import { compose, inputIntent } from '@/utils'; import { compose, inputIntent } from '@/utils';
import { TaxRatesSelect } from '@/components/TaxRates/TaxRatesSelect';
/** /**
* Item form body. * Item form body.
*/ */
function ItemFormBody({ organization: { base_currency } }) { function ItemFormBody({ organization: { base_currency } }) {
const { accounts, taxRates } = useItemFormContext(); const { accounts } = useItemFormContext();
const { values } = useFormikContext(); const { values } = useFormikContext();
return ( return (
@@ -113,20 +111,7 @@ function ItemFormBody({ organization: { base_currency } }) {
filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]} filterByParentTypes={[ACCOUNT_PARENT_TYPE.INCOME]}
fill={true} fill={true}
allowCreate={true} allowCreate={true}
fastField={true} fastField={true}
/>
</FFormGroup>
{/*------------- Sell Tax Rate ------------- */}
<FFormGroup
name={'sell_tax_rate_id'}
label={'Tax Rate'}
inline={true}
>
<TaxRatesSelect
name={'sell_tax_rate_id'}
items={taxRates}
allowCreate
/> />
</FFormGroup> </FFormGroup>
@@ -228,24 +213,6 @@ function ItemFormBody({ organization: { base_currency } }) {
/> />
</FFormGroup> </FFormGroup>
{/*------------- Purchase Tax Rate ------------- */}
<FFormGroup
name={'purchase_tax_rate_id'}
label={'Tax Rate'}
inline={true}
fastField={true}
shouldUpdateDeps={{ taxRates }}
shouldUpdate={taxRateFieldShouldUpdate}
>
<TaxRatesSelect
name={'purchase_tax_rate_id'}
items={taxRates}
allowCreate={true}
fastField={true}
shouldUpdateDeps={{ taxRates }}
/>
</FFormGroup>
<FastField <FastField
name={'purchase_description'} name={'purchase_description'}
purchasable={values.purchasable} purchasable={values.purchasable}

View File

@@ -10,7 +10,6 @@ import {
useAccounts, useAccounts,
} from '@/hooks/query'; } from '@/hooks/query';
import { useWatchItemError } from './utils'; import { useWatchItemError } from './utils';
import { useTaxRates } from '@/hooks/query/taxRates';
const ItemFormContext = createContext(); const ItemFormContext = createContext();
@@ -31,8 +30,6 @@ function ItemFormProvider({ itemId, ...props }) {
data: { itemsCategories }, data: { itemsCategories },
} = useItemsCategories(); } = useItemsCategories();
const { data: taxRates, isLoading: isTaxRatesLoading } = useTaxRates();
// Fetches the given item details. // Fetches the given item details.
const itemQuery = useItem(itemId || duplicateId, { const itemQuery = useItem(itemId || duplicateId, {
enabled: !!itemId || !!duplicateId, enabled: !!itemId || !!duplicateId,
@@ -72,7 +69,6 @@ function ItemFormProvider({ itemId, ...props }) {
accounts, accounts,
item, item,
itemsCategories, itemsCategories,
taxRates,
submitPayload, submitPayload,
isNewMode, isNewMode,
@@ -80,7 +76,6 @@ function ItemFormProvider({ itemId, ...props }) {
isAccountsLoading, isAccountsLoading,
isItemsCategoriesLoading, isItemsCategoriesLoading,
isItemLoading, isItemLoading,
isTaxRatesLoading,
createItemMutate, createItemMutate,
editItemMutate, editItemMutate,

View File

@@ -23,14 +23,12 @@ const defaultInitialValues = {
sell_price: '', sell_price: '',
cost_account_id: '', cost_account_id: '',
sell_account_id: '', sell_account_id: '',
sell_tax_rate_id: '',
inventory_account_id: '', inventory_account_id: '',
category_id: '', category_id: '',
sellable: 1, sellable: 1,
purchasable: true, purchasable: true,
sell_description: '', sell_description: '',
purchase_description: '', purchase_description: '',
purchase_tax_rate_id: '',
}; };
/** /**
@@ -189,13 +187,6 @@ export const purchaseDescFieldShouldUpdate = (newProps, oldProps) => {
); );
}; };
export const taxRateFieldShouldUpdate = (newProps, oldProps) => {
return (
newProps.shouldUpdateDeps.taxRates !== oldProps.shouldUpdateDeps.taxRates ||
defaultFastFieldShouldUpdate(newProps, oldProps)
);
};
export function transformItemsTableState(tableState) { export function transformItemsTableState(tableState) {
return { return {
...transformTableStateToQuery(tableState), ...transformTableStateToQuery(tableState),

Some files were not shown because too many files have changed in this diff Show More