Skip to content

Commit

Permalink
Merge pull request #545 from bigcapitalhq/excessed-payments-as-credit
Browse files Browse the repository at this point in the history
Excessed payments as credit
  • Loading branch information
abouolia authored Jul 25, 2024
2 parents 6b6b73b + 6d17f9c commit b93cb54
Show file tree
Hide file tree
Showing 34 changed files with 605 additions and 100 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

All notable changes to Bigcapital server-side will be in this file.

## [v0.18.0] - 10-08-2024

* feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
* feat: Categorize & match bank transaction by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
* feat: Reconcile match transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/522
* fix: Issues in matching transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/523
* fix: Cashflow transactions types by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/524

## [v0.17.5] - 17-06-2024

* fix: Balance sheet and P/L nested accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/501
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,15 @@ export default class BillsPayments extends BaseController {
check('vendor_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),

check('amount').exists().isNumeric().toFloat(),
check('payment_account_id').exists().isNumeric().toInt(),
check('payment_number').optional({ nullable: true }).trim().escape(),
check('payment_date').exists(),
check('statement').optional().trim().escape(),
check('reference').optional().trim().escape(),
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),

check('entries').exists().isArray({ min: 1 }),
check('entries').exists().isArray(),
check('entries.*.index').optional().isNumeric().toInt(),
check('entries.*.bill_id').exists().isNumeric().toInt(),
check('entries.*.payment_amount').exists().isNumeric().toFloat(),
Expand Down
4 changes: 2 additions & 2 deletions packages/server/src/api/controllers/Sales/PaymentReceives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export default class PaymentReceivesController extends BaseController {
check('customer_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),

check('amount').exists().isNumeric().toFloat(),
check('payment_date').exists(),
check('reference_no').optional(),
check('deposit_account_id').exists().isNumeric().toInt(),
Expand All @@ -158,8 +159,7 @@ export default class PaymentReceivesController extends BaseController {

check('branch_id').optional({ nullable: true }).isNumeric().toInt(),

check('entries').isArray({ min: 1 }),

check('entries').isArray({}),
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
check('entries.*.index').optional().isNumeric().toInt(),
check('entries.*.invoice_id').exists().isNumeric().toInt(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ export default class SeedAccounts extends TenantSeeder {
description: this.i18n.__(account.description),
currencyCode: this.tenant.metadata.baseCurrency,
seededAt: new Date(),
})
);
}));
return knex('accounts').then(async () => {
// Inserts seed entries.
return knex('accounts').insert(data);
Expand Down
24 changes: 24 additions & 0 deletions packages/server/src/database/seeds/data/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ export const TaxPayableAccount = {
predefined: 1,
};

export const UnearnedRevenueAccount = {
name: 'Unearned Revenue',
slug: 'unearned-revenue',
account_type: 'other-current-liability',
parent_account_id: null,
code: '50005',
active: true,
index: 1,
predefined: true,
};

export const PrepardExpenses = {
name: 'Prepaid Expenses',
slug: 'prepaid-expenses',
account_type: 'other-current-asset',
parent_account_id: null,
code: '100010',
active: true,
index: 1,
predefined: true,
};

export default [
{
name: 'Bank Account',
Expand Down Expand Up @@ -323,4 +345,6 @@ export default [
index: 1,
predefined: 0,
},
UnearnedRevenueAccount,
PrepardExpenses,
];
2 changes: 1 addition & 1 deletion packages/server/src/interfaces/Ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export interface ILedgerEntry {
date: Date | string;

transactionType: string;
transactionSubType: string;
transactionSubType?: string;

transactionId: number;

Expand Down
4 changes: 2 additions & 2 deletions packages/server/src/models/Bill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,9 +525,9 @@ export default class Bill extends mixin(TenantModel, [
return notFoundBillsIds;
}

static changePaymentAmount(billId, amount) {
static changePaymentAmount(billId, amount, trx) {
const changeMethod = amount > 0 ? 'increment' : 'decrement';
return this.query()
return this.query(trx)
.where('id', billId)
[changeMethod]('payment_amount', Math.abs(amount));
}
Expand Down
70 changes: 69 additions & 1 deletion packages/server/src/repositories/AccountRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { Account } from 'models';
import TenantRepository from '@/repositories/TenantRepository';
import { IAccount } from '@/interfaces';
import { Knex } from 'knex';
import { TaxPayableAccount } from '@/database/seeds/data/accounts';
import {
PrepardExpenses,
TaxPayableAccount,
UnearnedRevenueAccount,
} from '@/database/seeds/data/accounts';
import { TenantMetadata } from '@/system/models';

export default class AccountRepository extends TenantRepository {
/**
Expand Down Expand Up @@ -179,4 +184,67 @@ export default class AccountRepository extends TenantRepository {
}
return result;
};

/**
* Finds or creates the unearned revenue.
* @param {Record<string, string>} extraAttrs
* @param {Knex.Transaction} trx
* @returns
*/
public async findOrCreateUnearnedRevenue(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
// Retrieves the given tenant metadata.
const tenantMeta = await TenantMetadata.query().findOne({
tenantId: this.tenantId,
});
const _extraAttrs = {
currencyCode: tenantMeta.baseCurrency,
...extraAttrs,
};
let result = await this.model
.query(trx)
.findOne({ slug: UnearnedRevenueAccount.slug, ..._extraAttrs });

if (!result) {
result = await this.model.query(trx).insertAndFetch({
...UnearnedRevenueAccount,
..._extraAttrs,
});
}
return result;
}

/**
* Finds or creates the prepard expenses account.
* @param {Record<string, string>} extraAttrs
* @param {Knex.Transaction} trx
* @returns
*/
public async findOrCreatePrepardExpenses(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
// Retrieves the given tenant metadata.
const tenantMeta = await TenantMetadata.query().findOne({
tenantId: this.tenantId,
});
const _extraAttrs = {
currencyCode: tenantMeta.baseCurrency,
...extraAttrs,
};

let result = await this.model
.query(trx)
.findOne({ slug: PrepardExpenses.slug, ..._extraAttrs });

if (!result) {
result = await this.model.query(trx).insertAndFetch({
...PrepardExpenses,
..._extraAttrs,
});
}
return result;
}
}
11 changes: 8 additions & 3 deletions packages/server/src/repositories/TenantRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import CachableRepository from './CachableRepository';

export default class TenantRepository extends CachableRepository {
repositoryName: string;

tenantId: number;

/**
* Constructor method.
* @param {number} tenantId
* @param {number} tenantId
*/
constructor(knex, cache, i18n) {
super(knex, cache, i18n);
}
}

setTenantId(tenantId: number) {
this.tenantId = tenantId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export class CustomersApplication {

/**
* Creates a new customer.
* @param {number} tenantId
* @param {ICustomerNewDTO} customerDTO
* @param {ISystemUser} authorizedUser
* @param {number} tenantId
* @param {ICustomerNewDTO} customerDTO
* @param {ISystemUser} authorizedUser
* @returns {Promise<ICustomer>}
*/
public createCustomer = (tenantId: number, customerDTO: ICustomerNewDTO) => {
Expand All @@ -56,9 +56,9 @@ export class CustomersApplication {

/**
* Edits details of the given customer.
* @param {number} tenantId
* @param {number} customerId
* @param {ICustomerEditDTO} customerDTO
* @param {number} tenantId
* @param {number} customerId
* @param {ICustomerEditDTO} customerDTO
* @return {Promise<ICustomer>}
*/
public editCustomer = (
Expand All @@ -75,9 +75,9 @@ export class CustomersApplication {

/**
* Deletes the given customer and associated transactions.
* @param {number} tenantId
* @param {number} customerId
* @param {ISystemUser} authorizedUser
* @param {number} tenantId
* @param {number} customerId
* @param {ISystemUser} authorizedUser
* @returns {Promise<void>}
*/
public deleteCustomer = (
Expand All @@ -94,9 +94,9 @@ export class CustomersApplication {

/**
* Changes the opening balance of the given customer.
* @param {number} tenantId
* @param {number} customerId
* @param {Date|string} openingBalanceEditDTO
* @param {number} tenantId
* @param {number} customerId
* @param {Date|string} openingBalanceEditDTO
* @returns {Promise<ICustomer>}
*/
public editOpeningBalance = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { omit, sumBy } from 'lodash';
import { IBillPayment, IBillPaymentDTO, IVendor } from '@/interfaces';
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
import { formatDateFields } from '@/utils';
import HasTenancyService from '@/services/Tenancy/TenancyService';

@Service()
export class CommandBillPaymentDTOTransformer {
Expand All @@ -23,11 +24,14 @@ export class CommandBillPaymentDTOTransformer {
vendor: IVendor,
oldBillPayment?: IBillPayment
): Promise<IBillPayment> {
const amount =
billPaymentDTO.amount ?? sumBy(billPaymentDTO.entries, 'paymentAmount');

const initialDTO = {
...formatDateFields(omit(billPaymentDTO, ['attachments']), [
'paymentDate',
]),
amount: sumBy(billPaymentDTO.entries, 'paymentAmount'),
amount,
currencyCode: vendor.currencyCode,
exchangeRate: billPaymentDTO.exchangeRate || 1,
entries: billPaymentDTO.entries,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ export class PaymentReceiveDTOTransformer {
paymentReceiveDTO: IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO,
oldPaymentReceive?: IPaymentReceive
): Promise<IPaymentReceive> {
const paymentAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
const amount =
paymentReceiveDTO.amount ??
sumBy(paymentReceiveDTO.entries, 'paymentAmount');

// Retreive the next invoice number.
const autoNextNumber =
Expand All @@ -54,7 +56,7 @@ export class PaymentReceiveDTOTransformer {
...formatDateFields(omit(paymentReceiveDTO, ['entries', 'attachments']), [
'paymentDate',
]),
amount: paymentAmount,
amount,
currencyCode: customer.currencyCode,
...(paymentReceiveNo ? { paymentReceiveNo } : {}),
exchangeRate: paymentReceiveDTO.exchangeRate || 1,
Expand Down
7 changes: 6 additions & 1 deletion packages/server/src/services/Tenancy/TenancyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ export default class HasTenancyService {
const knex = this.knex(tenantId);
const i18n = this.i18n(tenantId);

return tenantRepositoriesLoader(knex, cache, i18n);
const repositories = tenantRepositoriesLoader(knex, cache, i18n);

Object.values(repositories).forEach((repository) => {
repository.setTenantId(tenantId);
});
return repositories;
});
}

Expand Down
4 changes: 2 additions & 2 deletions packages/webapp/src/constants/accountTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ export const ACCOUNT_TYPE = {
BANK: 'bank',
ACCOUNTS_RECEIVABLE: 'accounts-receivable',
INVENTORY: 'inventory',
OTHER_CURRENT_ASSET: 'other-ACCOUNT_PARENT_TYPE.CURRENT_ASSET',
OTHER_CURRENT_ASSET: 'other-current-asset',
FIXED_ASSET: 'fixed-asset',
NON_CURRENT_ASSET: 'non-ACCOUNT_PARENT_TYPE.CURRENT_ASSET',
NON_CURRENT_ASSET: 'non-current-asset',

ACCOUNTS_PAYABLE: 'accounts-payable',
CREDIT_CARD: 'credit-card',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ExcessPaymentDialog } from './dialogs/PaymentMadeExcessDialog';

export function PaymentMadeDialogs() {
return (
<>
<ExcessPaymentDialog dialogName={'payment-made-excessed-payment'} />
</>
);
}
Loading

0 comments on commit b93cb54

Please sign in to comment.