mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
Compare commits
6 Commits
dev/tax-nu
...
all-contri
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44bd916ede | ||
|
|
fd5db4f055 | ||
|
|
e4a7f09dbc | ||
|
|
2c5537efad | ||
|
|
017908600e | ||
|
|
6307ca8935 |
@@ -60,6 +60,15 @@
|
||||
"contributions": [
|
||||
"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,
|
||||
|
||||
@@ -80,6 +80,7 @@ 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://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://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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
867
packages/server/package-lock.json
generated
867
packages/server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
@@ -36,7 +36,7 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
* Validation schema.
|
||||
* @return {ValidationChain[]}
|
||||
*/
|
||||
get trialBalanceSheetValidationSchema(): ValidationChain[] {
|
||||
private get trialBalanceSheetValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
...this.sheetNumberFormatValidationSchema,
|
||||
query('basis').optional(),
|
||||
@@ -59,28 +59,37 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
/**
|
||||
* Retrieve the trial balance sheet.
|
||||
*/
|
||||
public async trialBalanceSheet(
|
||||
private async trialBalanceSheet(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId, settings } = req;
|
||||
const { tenantId } = req;
|
||||
let filter = this.matchedQueryData(req);
|
||||
|
||||
filter = {
|
||||
...filter,
|
||||
accountsIds: castArray(filter.accountsIds),
|
||||
};
|
||||
|
||||
try {
|
||||
const { data, query, meta } =
|
||||
await this.trialBalanceSheetService.trialBalanceSheet(tenantId, filter);
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
return res.status(200).send({
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
meta: this.transfromToResponse(meta),
|
||||
});
|
||||
if (acceptType === 'application/json+table') {
|
||||
const { table, meta, query } =
|
||||
await this.trialBalanceSheetService.trialBalanceSheetTable(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ table, meta, query });
|
||||
} else {
|
||||
const { data, query, meta } =
|
||||
await this.trialBalanceSheetService.trialBalanceSheet(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ data, query, meta });
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ export default class OrganizationController extends BaseController {
|
||||
|
||||
router.post(
|
||||
'/build',
|
||||
this.organizationValidationSchema,
|
||||
this.buildOrganizationValidationSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.build.bind(this)),
|
||||
this.handleServiceErrors.bind(this)
|
||||
);
|
||||
router.put(
|
||||
'/',
|
||||
this.organizationValidationSchema,
|
||||
this.updateOrganizationValidationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.updateOrganization.bind(this)),
|
||||
this.handleServiceErrors.bind(this)
|
||||
@@ -55,7 +55,7 @@ export default class OrganizationController extends BaseController {
|
||||
* Organization setup schema.
|
||||
* @return {ValidationChain[]}
|
||||
*/
|
||||
private get organizationValidationSchema(): ValidationChain[] {
|
||||
private get commonOrganizationValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('name').exists().trim(),
|
||||
check('industry').optional({ nullable: true }).isString().trim().escape(),
|
||||
@@ -68,6 +68,29 @@ 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.
|
||||
* @param {Request} req - Express request.
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface ILedger {
|
||||
|
||||
getClosingBalance(): number;
|
||||
getForeignClosingBalance(): number;
|
||||
getClosingDebit(): number;
|
||||
getClosingCredit(): number;
|
||||
|
||||
getContactsIds(): number[];
|
||||
getAccountsIds(): number[];
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface IOrganizationUpdateDTO {
|
||||
timezone: string;
|
||||
fiscalYear: string;
|
||||
industry: string;
|
||||
taxNumber: string;
|
||||
}
|
||||
|
||||
export interface IOrganizationBuildEventPayload {
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface ITrialBalanceAccount extends ITrialBalanceTotal {
|
||||
id: number;
|
||||
parentAccountId: number;
|
||||
name: string;
|
||||
formattedName: string;
|
||||
code: string;
|
||||
accountNormal: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import moment from 'moment';
|
||||
import { defaultTo, uniqBy } from 'lodash';
|
||||
import { defaultTo, sumBy, uniqBy } from 'lodash';
|
||||
import { IAccountTransaction, ILedger, ILedgerEntry } from '@/interfaces';
|
||||
|
||||
export default class Ledger implements ILedger {
|
||||
@@ -49,6 +49,15 @@ export default class Ledger implements ILedger {
|
||||
return this.filter((entry) => entry.accountId === accountId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters entries by the given accounts ids then returns a new ledger.
|
||||
* @param {number[]} accountsIds - Accounts ids.
|
||||
* @returns {ILedger}
|
||||
*/
|
||||
public whereAccountsIds(accountsIds: number[]): ILedger {
|
||||
return this.filter((entry) => accountsIds.indexOf(entry.accountId) !== -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters entries that before or same the given date and returns a new ledger.
|
||||
* @param {Date|string} fromDate
|
||||
@@ -130,6 +139,22 @@ export default class Ledger implements ILedger {
|
||||
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.
|
||||
* @returns {number}
|
||||
|
||||
@@ -10,13 +10,26 @@ import {
|
||||
} from '@/interfaces';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { allPassedConditionsPass, flatToNestedArray } from 'utils';
|
||||
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
|
||||
|
||||
export default class TrialBalanceSheet extends FinancialSheet {
|
||||
tenantId: number;
|
||||
query: ITrialBalanceSheetQuery;
|
||||
accounts: IAccount & { type: IAccountType }[];
|
||||
journalFinancial: any;
|
||||
baseCurrency: string;
|
||||
/**
|
||||
* Trial balance sheet query.
|
||||
* @param {ITrialBalanceSheetQuery} query
|
||||
*/
|
||||
private query: ITrialBalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Trial balance sheet repository.
|
||||
* @param {TrialBalanceSheetRepository}
|
||||
*/
|
||||
private repository: TrialBalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Organization base currency.
|
||||
* @param {string}
|
||||
*/
|
||||
private baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -28,20 +41,58 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
constructor(
|
||||
tenantId: number,
|
||||
query: ITrialBalanceSheetQuery,
|
||||
accounts: IAccount & { type: IAccountType }[],
|
||||
journalFinancial: any,
|
||||
repository: TrialBalanceSheetRepository,
|
||||
baseCurrency: string
|
||||
) {
|
||||
super();
|
||||
|
||||
this.tenantId = tenantId;
|
||||
this.query = query;
|
||||
this.accounts = accounts;
|
||||
this.journalFinancial = journalFinancial;
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
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.
|
||||
* @param {IAccount} account
|
||||
@@ -50,23 +101,28 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
private accountTransformer = (
|
||||
account: IAccount & { type: IAccountType }
|
||||
): ITrialBalanceAccount => {
|
||||
const trial = this.journalFinancial.getTrialBalanceWithDepands(account.id);
|
||||
const debit = this.getClosingAccountDebit(account.id);
|
||||
const credit = this.getClosingAccountCredit(account.id);
|
||||
const balance = this.getClosingAccountTotal(account.id);
|
||||
|
||||
return {
|
||||
id: account.id,
|
||||
parentAccountId: account.parentAccountId,
|
||||
name: account.name,
|
||||
formattedName: account.code
|
||||
? `${account.name} - ${account.code}`
|
||||
: `${account.name}`,
|
||||
code: account.code,
|
||||
accountNormal: account.accountNormal,
|
||||
|
||||
credit: trial.credit,
|
||||
debit: trial.debit,
|
||||
balance: trial.balance,
|
||||
credit,
|
||||
debit,
|
||||
balance,
|
||||
currencyCode: this.baseCurrency,
|
||||
|
||||
formattedCredit: this.formatNumber(trial.credit),
|
||||
formattedDebit: this.formatNumber(trial.debit),
|
||||
formattedBalance: this.formatNumber(trial.balance),
|
||||
formattedCredit: this.formatNumber(credit),
|
||||
formattedDebit: this.formatNumber(debit),
|
||||
formattedBalance: this.formatNumber(balance),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -117,10 +173,7 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
private filterNoneTransactions = (
|
||||
accountNode: ITrialBalanceAccount
|
||||
): boolean => {
|
||||
const entries = this.journalFinancial.getAccountEntriesWithDepents(
|
||||
accountNode.id
|
||||
);
|
||||
return entries.length > 0;
|
||||
return false === this.repository.totalAccountsLedger.isEmpty();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -200,11 +253,11 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
*/
|
||||
public reportData(): ITrialBalanceSheetData {
|
||||
// Don't return noting if the journal has no transactions.
|
||||
if (this.journalFinancial.isEmpty()) {
|
||||
if (this.repository.totalAccountsLedger.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// Retrieve accounts nodes.
|
||||
const accounts = this.accountsSection(this.accounts);
|
||||
const accounts = this.accountsSection(this.repository.accounts);
|
||||
|
||||
// Retrieve account node.
|
||||
const total = this.tatalSection(accounts);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2,12 +2,18 @@ import { Service, Inject } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import Journal from '@/services/Accounting/JournalPoster';
|
||||
import { ITrialBalanceSheetMeta, ITrialBalanceSheetQuery, ITrialBalanceStatement } from '@/interfaces';
|
||||
import {
|
||||
ITrialBalanceSheetMeta,
|
||||
ITrialBalanceSheetQuery,
|
||||
ITrialBalanceStatement,
|
||||
} from '@/interfaces';
|
||||
import TrialBalanceSheet from './TrialBalanceSheet';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import { parseBoolean } from 'utils';
|
||||
import { Tenant } from '@/system/models';
|
||||
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
|
||||
import { TrialBalanceSheetTable } from './TrialBalanceSheetTable';
|
||||
|
||||
@Service()
|
||||
export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
@@ -51,9 +57,8 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
reportMetadata(tenantId: number): ITrialBalanceSheetMeta {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
|
||||
const isCostComputeRunning = this.inventoryService.isItemsCostComputeRunning(
|
||||
tenantId
|
||||
);
|
||||
const isCostComputeRunning =
|
||||
this.inventoryService.isItemsCostComputeRunning(tenantId);
|
||||
const organizationName = settings.get({
|
||||
group: 'organization',
|
||||
key: 'name',
|
||||
@@ -72,10 +77,8 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
|
||||
/**
|
||||
* Retrieve trial balance sheet statement.
|
||||
* -------------
|
||||
* @param {number} tenantId
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*
|
||||
* @return {IBalanceSheetStatement}
|
||||
*/
|
||||
public async trialBalanceSheet(
|
||||
@@ -86,43 +89,27 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
...this.defaultQuery,
|
||||
...query,
|
||||
};
|
||||
const {
|
||||
accountRepository,
|
||||
transactionsRepository,
|
||||
} = this.tenancy.repositories(tenantId);
|
||||
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
this.logger.info('[trial_balance_sheet] trying to calcualte the report.', {
|
||||
tenantId,
|
||||
filter,
|
||||
});
|
||||
// Retrieve all accounts on the storage.
|
||||
const accounts = await accountRepository.all();
|
||||
const accountsGraph = await accountRepository.getDependencyGraph();
|
||||
const models = this.tenancy.models(tenantId);
|
||||
const repos = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Retrieve all journal transactions based on the given query.
|
||||
const transactions = await transactionsRepository.journal({
|
||||
fromDate: query.fromDate,
|
||||
toDate: query.toDate,
|
||||
sumationCreditDebit: true,
|
||||
branchesIds: query.branchesIds
|
||||
});
|
||||
// Transform transactions array to journal collection.
|
||||
const transactionsJournal = Journal.fromTransactions(
|
||||
transactions,
|
||||
tenantId,
|
||||
accountsGraph
|
||||
const trialBalanceSheetRepos = new TrialBalanceSheetRepository(
|
||||
models,
|
||||
repos,
|
||||
filter
|
||||
);
|
||||
await trialBalanceSheetRepos.asyncInitialize();
|
||||
|
||||
// Trial balance report instance.
|
||||
const trialBalanceInstance = new TrialBalanceSheet(
|
||||
tenantId,
|
||||
filter,
|
||||
accounts,
|
||||
transactionsJournal,
|
||||
tenant.metadata.baseCurrency,
|
||||
trialBalanceSheetRepos,
|
||||
tenant.metadata.baseCurrency
|
||||
);
|
||||
// Trial balance sheet data.
|
||||
const trialBalanceSheetData = trialBalanceInstance.reportData();
|
||||
@@ -133,4 +120,27 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
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' },
|
||||
])
|
||||
)([]);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
export enum IROW_TYPE {
|
||||
ACCOUNT = 'ACCOUNT',
|
||||
TOTAL = 'TOTAL',
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
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');
|
||||
});
|
||||
};
|
||||
@@ -62,6 +62,7 @@ const BalanceSheetDataTable = styled(ReportDataTable)`
|
||||
border-bottom: 0;
|
||||
padding-top: 0.32rem;
|
||||
padding-bottom: 0.32rem;
|
||||
color: #252A31;
|
||||
}
|
||||
&.is-expanded {
|
||||
.td:not(.name) .cell-inner {
|
||||
@@ -72,6 +73,7 @@ const BalanceSheetDataTable = styled(ReportDataTable)`
|
||||
.td {
|
||||
font-weight: 500;
|
||||
border-top: 1px solid #bbb;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,9 +60,8 @@ const CashflowStatementDataTable = styled(DataTable)`
|
||||
border-bottom: 0;
|
||||
padding-top: 0.32rem;
|
||||
padding-bottom: 0.32rem;
|
||||
color: #252a31;
|
||||
}
|
||||
|
||||
// &.row-type--AGGREGATE,
|
||||
&.row_type--ACCOUNTS {
|
||||
border-top: 1px solid #bbb;
|
||||
}
|
||||
@@ -72,6 +71,9 @@ const CashflowStatementDataTable = styled(DataTable)`
|
||||
&.row_type--TOTAL {
|
||||
font-weight: 500;
|
||||
|
||||
.td {
|
||||
color: #000;
|
||||
}
|
||||
&:not(:first-child) .td {
|
||||
border-top: 1px solid #bbb;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ const ProfitLossDataTable = styled(ReportDataTable)`
|
||||
border-bottom: 0;
|
||||
padding-top: 0.32rem;
|
||||
padding-bottom: 0.32rem;
|
||||
color: #252A31;
|
||||
}
|
||||
&.is-expanded {
|
||||
.td:not(.name) .cell-inner {
|
||||
@@ -71,6 +72,7 @@ const ProfitLossDataTable = styled(ReportDataTable)`
|
||||
.td {
|
||||
font-weight: 500;
|
||||
border-top: 1px solid #bbb;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
&:last-of-type .td {
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from './components';
|
||||
|
||||
import withTrialBalanceActions from './withTrialBalanceActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,8 +8,7 @@ import { tableRowTypesToClassnames } from '@/utils';
|
||||
import { ReportDataTable, FinancialSheet } from '@/components';
|
||||
|
||||
import { useTrialBalanceSheetContext } from './TrialBalanceProvider';
|
||||
import { useTrialBalanceTableColumns } from './components';
|
||||
|
||||
import { useTrialBalanceSheetTableColumns } from './hooks';
|
||||
|
||||
/**
|
||||
* Trial Balance sheet data table.
|
||||
@@ -17,12 +16,12 @@ import { useTrialBalanceTableColumns } from './components';
|
||||
export default function TrialBalanceSheetTable({ companyName }) {
|
||||
// Trial balance sheet context.
|
||||
const {
|
||||
trialBalanceSheet: { tableRows, query },
|
||||
trialBalanceSheet: { table, query },
|
||||
isLoading,
|
||||
} = useTrialBalanceSheetContext();
|
||||
|
||||
// Trial balance sheet table columns.
|
||||
const columns = useTrialBalanceTableColumns();
|
||||
const columns = useTrialBalanceSheetTableColumns();
|
||||
|
||||
return (
|
||||
<FinancialSheet
|
||||
@@ -36,7 +35,7 @@ export default function TrialBalanceSheetTable({ companyName }) {
|
||||
>
|
||||
<TrialBalanceDataTable
|
||||
columns={columns}
|
||||
data={tableRows}
|
||||
data={table.rows}
|
||||
expandable={true}
|
||||
expandToggleColumn={1}
|
||||
expandColumnSpace={1}
|
||||
@@ -59,7 +58,7 @@ const TrialBalanceDataTable = styled(ReportDataTable)`
|
||||
.balance.td {
|
||||
border-top-color: #000;
|
||||
}
|
||||
.tr.row_type--total .td {
|
||||
.tr.row_type--TOTAL .td {
|
||||
border-top: 1px solid #bbb;
|
||||
font-weight: 500;
|
||||
border-bottom: 3px double #000;
|
||||
|
||||
@@ -1,88 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
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 { useTrialBalanceSheetContext } from './TrialBalanceProvider';
|
||||
import { FinancialComputeAlert } from '../FinancialReportPage';
|
||||
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.
|
||||
*/
|
||||
@@ -101,7 +23,7 @@ export function TrialBalanceSheetLoadingBar() {
|
||||
*/
|
||||
export function TrialBalanceSheetAlerts() {
|
||||
const {
|
||||
trialBalanceSheet: { meta },
|
||||
trialBalanceSheet,
|
||||
isLoading,
|
||||
refetchSheet,
|
||||
} = useTrialBalanceSheetContext();
|
||||
@@ -115,7 +37,7 @@ export function TrialBalanceSheetAlerts() {
|
||||
return null;
|
||||
}
|
||||
// Can't continue if the cost compute job is not running.
|
||||
if (!meta.is_cost_compute_running) {
|
||||
if (!trialBalanceSheet?.meta.is_cost_compute_running) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// @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);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// @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],
|
||||
);
|
||||
};
|
||||
@@ -6,6 +6,9 @@ const Schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.required()
|
||||
.label(intl.get('organization_name_')),
|
||||
tax_number: Yup.string()
|
||||
.nullable()
|
||||
.label(intl.get('organization_tax_number_')),
|
||||
industry: Yup.string()
|
||||
.nullable()
|
||||
.label(intl.get('organization_industry_')),
|
||||
|
||||
@@ -59,6 +59,17 @@ export default function PreferencesGeneralForm({ isSubmitting }) {
|
||||
<FInputGroup medium={'true'} name={'name'} fastField={true} />
|
||||
</FFormGroup>
|
||||
|
||||
{/* ---------- Organization Tax Number ---------- */}
|
||||
<FFormGroup
|
||||
name={'tax_number'}
|
||||
label={<T id={'organization_tax_number'} />}
|
||||
inline={true}
|
||||
helperText={<T id={'shown_on_sales_forms_and_purchase_orders'} />}
|
||||
fastField={true}
|
||||
>
|
||||
<FInputGroup medium={'true'} name={'tax_number'} fastField={true} />
|
||||
</FFormGroup>
|
||||
|
||||
{/* ---------- Industry ---------- */}
|
||||
<FFormGroup
|
||||
name={'industry'}
|
||||
|
||||
@@ -23,6 +23,7 @@ const defaultValues = {
|
||||
fiscal_year: '',
|
||||
date_format: '',
|
||||
timezone: '',
|
||||
tax_number: '',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,17 +43,12 @@ export function useTrialBalanceSheet(query, props) {
|
||||
method: 'get',
|
||||
url: '/financial_statements/trial_balance_sheet',
|
||||
params: query,
|
||||
headers: {
|
||||
Accept: 'application/json+table',
|
||||
},
|
||||
},
|
||||
{
|
||||
select: (res) => ({
|
||||
tableRows: trialBalanceSheetReducer(res.data.data),
|
||||
...res.data,
|
||||
}),
|
||||
defaultData: {
|
||||
tableRows: [],
|
||||
data: [],
|
||||
query: {},
|
||||
},
|
||||
select: (res) => res.data,
|
||||
...props,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"success": "Success",
|
||||
"register_a_new_organization": "Register a New Organization.",
|
||||
"organization_name": "Organization Name",
|
||||
"organization_tax_number": "Organization Tax Number",
|
||||
"email": "Email",
|
||||
"email_address": "Email Address",
|
||||
"register": "Register",
|
||||
@@ -339,6 +340,7 @@
|
||||
"item_type_": "Item type",
|
||||
"item_name_": "Item name",
|
||||
"organization_industry_": "Organization industry",
|
||||
"organization_tax_number_": "Organization tax number",
|
||||
"base_currency_": "Base currency",
|
||||
"date_format_": "Date format",
|
||||
"category_name_": "Category name",
|
||||
@@ -1804,6 +1806,7 @@
|
||||
"balance_sheet.total_change": "Total Change",
|
||||
"balance_sheet.change": "% Change",
|
||||
"balance_sheet.previous_period": "Previous Period (PP)",
|
||||
"balance_sheet.net_income": "Net Income",
|
||||
"profit_loss_sheet.comparisons": "Comparisons",
|
||||
"profit_loss_sheet.dimensions": "Dimensions",
|
||||
"profit_loss_sheet.previous_year": "Previous Year",
|
||||
|
||||
Reference in New Issue
Block a user