mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-12 10:50:31 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
415673656c | ||
|
|
1906d9f3f5 | ||
|
|
d640dc1f40 | ||
|
|
8cd1b36a02 | ||
|
|
5a8d9cc7e8 | ||
|
|
6323e2ffec | ||
|
|
7af2e7ccbc |
@@ -2,6 +2,15 @@
|
||||
|
||||
All notable changes to Bigcapital server-side will be in this file.
|
||||
|
||||
# [0.22.0]
|
||||
|
||||
* feat: estimate, receipt, credit note mail preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/757
|
||||
* feat: Add discount to transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/758
|
||||
* fix: update financial forms to use new formatted amount utilities and… by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/760
|
||||
* fix: total lines style by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/761
|
||||
* fix: discount & adjustment sale transactions bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/762
|
||||
* fix: discount transactions GL entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/763
|
||||
|
||||
# [0.21.2]
|
||||
|
||||
* hotbug: upload attachments by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/755
|
||||
|
||||
@@ -127,6 +127,11 @@ export default class BillsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.discount_type')
|
||||
.default(DiscountType.Percentage)
|
||||
.isString()
|
||||
.isIn([DiscountType.Percentage, DiscountType.Amount]),
|
||||
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.landed_cost')
|
||||
.optional({ nullable: true })
|
||||
|
||||
@@ -176,6 +176,10 @@ export default class VendorCreditController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.discount_type')
|
||||
.default(DiscountType.Percentage)
|
||||
.isString()
|
||||
.isIn([DiscountType.Percentage, DiscountType.Amount]),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
@@ -225,6 +229,10 @@ export default class VendorCreditController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.discount_type')
|
||||
.default(DiscountType.Percentage)
|
||||
.isString()
|
||||
.isIn([DiscountType.Percentage, DiscountType.Amount]),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
|
||||
@@ -239,6 +239,10 @@ export default class PaymentReceivesController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.discount_type')
|
||||
.default(DiscountType.Percentage)
|
||||
.isString()
|
||||
.isIn([DiscountType.Percentage, DiscountType.Amount]),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
|
||||
@@ -187,6 +187,11 @@ export default class SalesEstimatesController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.discount_type')
|
||||
.default(DiscountType.Percentage)
|
||||
.isString()
|
||||
.isIn([DiscountType.Percentage, DiscountType.Amount]),
|
||||
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
|
||||
@@ -243,6 +243,10 @@ export default class SaleInvoicesController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toFloat(),
|
||||
check('entries.*.discount_type')
|
||||
.default(DiscountType.Percentage)
|
||||
.isString()
|
||||
.isIn([DiscountType.Percentage, DiscountType.Amount]),
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.tax_code')
|
||||
.optional({ nullable: true })
|
||||
|
||||
@@ -164,6 +164,11 @@ export default class SalesReceiptsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
check('entries.*.discount_type')
|
||||
.default(DiscountType.Percentage)
|
||||
.isString()
|
||||
.isIn([DiscountType.Percentage, DiscountType.Amount]),
|
||||
|
||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||
check('entries.*.warehouse_id')
|
||||
.optional({ nullable: true })
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.alterTable('items_entries', (table) => {
|
||||
table.string('discount_type').defaultTo('percentage').after('discount');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.alterTable('items_entries', (table) => {
|
||||
table.dropColumn('discount_type');
|
||||
});
|
||||
};
|
||||
@@ -13,14 +13,17 @@ export interface IItemEntry {
|
||||
|
||||
itemId: number;
|
||||
description: string;
|
||||
discountType?: string;
|
||||
discount: number;
|
||||
quantity: number;
|
||||
rate: number;
|
||||
amount: number;
|
||||
|
||||
total: number;
|
||||
amountInclusingTax: number;
|
||||
amountExludingTax: number;
|
||||
totalExcludingTax?: number;
|
||||
|
||||
subtotalInclusingTax: number;
|
||||
subtotalExcludingTax: number;
|
||||
discountAmount: number;
|
||||
|
||||
landedCost: number;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { Model } from 'objection';
|
||||
import TenantModel from 'models/TenantModel';
|
||||
import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '@/utils/taxRate';
|
||||
import { DiscountType } from '@/interfaces';
|
||||
|
||||
// Subtotal (qty * rate) (tax inclusive)
|
||||
// Subtotal Tax Exclusive (Subtotal - Tax Amount)
|
||||
// Discount (Is percentage ? amount * discount : discount)
|
||||
// Total (Subtotal - Discount)
|
||||
|
||||
export default class ItemEntry extends TenantModel {
|
||||
public taxRate: number;
|
||||
@@ -8,7 +14,7 @@ export default class ItemEntry extends TenantModel {
|
||||
public quantity: number;
|
||||
public rate: number;
|
||||
public isInclusiveTax: number;
|
||||
|
||||
public discountType: DiscountType;
|
||||
/**
|
||||
* Table name.
|
||||
* @returns {string}
|
||||
@@ -31,10 +37,24 @@ export default class ItemEntry extends TenantModel {
|
||||
*/
|
||||
static get virtualAttributes() {
|
||||
return [
|
||||
// Amount (qty * rate)
|
||||
'amount',
|
||||
|
||||
'taxAmount',
|
||||
'amountExludingTax',
|
||||
'amountInclusingTax',
|
||||
|
||||
// Subtotal (qty * rate) + (tax inclusive)
|
||||
'subtotalInclusingTax',
|
||||
|
||||
// Subtotal Tax Exclusive (Subtotal - Tax Amount)
|
||||
'subtotalExcludingTax',
|
||||
|
||||
// Subtotal (qty * rate) + (tax inclusive)
|
||||
'subtotal',
|
||||
|
||||
// Discount (Is percentage ? amount * discount : discount)
|
||||
'discountAmount',
|
||||
|
||||
// Total (Subtotal - Discount)
|
||||
'total',
|
||||
];
|
||||
}
|
||||
@@ -45,7 +65,15 @@ export default class ItemEntry extends TenantModel {
|
||||
* @returns {number}
|
||||
*/
|
||||
get total() {
|
||||
return this.amountInclusingTax;
|
||||
return this.subtotal - this.discountAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total (excluding tax).
|
||||
* @returns {number}
|
||||
*/
|
||||
get totalExcludingTax() {
|
||||
return this.subtotalExcludingTax - this.discountAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,19 +85,27 @@ export default class ItemEntry extends TenantModel {
|
||||
return this.quantity * this.rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtotal amount (tax inclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
get subtotal() {
|
||||
return this.subtotalInclusingTax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Item entry amount including tax.
|
||||
* @returns {number}
|
||||
*/
|
||||
get amountInclusingTax() {
|
||||
get subtotalInclusingTax() {
|
||||
return this.isInclusiveTax ? this.amount : this.amount + this.taxAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Item entry amount excluding tax.
|
||||
* Subtotal amount (tax exclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
get amountExludingTax() {
|
||||
get subtotalExcludingTax() {
|
||||
return this.isInclusiveTax ? this.amount - this.taxAmount : this.amount;
|
||||
}
|
||||
|
||||
@@ -78,7 +114,9 @@ export default class ItemEntry extends TenantModel {
|
||||
* @returns {number}
|
||||
*/
|
||||
get discountAmount() {
|
||||
return this.amount * (this.discount / 100);
|
||||
return this.discountType === DiscountType.Percentage
|
||||
? this.amount * (this.discount / 100)
|
||||
: this.discount;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -210,11 +210,11 @@ export default class CreditNoteGLEntries {
|
||||
index: number
|
||||
): ILedgerEntry => {
|
||||
const commonEntry = this.getCreditNoteCommonEntry(creditNote);
|
||||
const localAmount = entry.amount * creditNote.exchangeRate;
|
||||
const totalLocal = entry.totalExcludingTax * creditNote.exchangeRate;
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
debit: localAmount,
|
||||
debit: totalLocal,
|
||||
accountId: entry.sellAccountId || entry.item.sellAccountId,
|
||||
note: entry.description,
|
||||
index: index + 2,
|
||||
|
||||
@@ -139,13 +139,12 @@ export class BillGLEntries {
|
||||
private getBillItemEntry = R.curry(
|
||||
(bill: IBill, entry: IItemEntry, index: number): ILedgerEntry => {
|
||||
const commonJournalMeta = this.getBillCommonEntry(bill);
|
||||
|
||||
const localAmount = bill.exchangeRate * entry.amountExludingTax;
|
||||
const totalLocal = bill.exchangeRate * entry.totalExcludingTax;
|
||||
const landedCostAmount = sumBy(entry.allocatedCostEntries, 'cost');
|
||||
|
||||
return {
|
||||
...commonJournalMeta,
|
||||
debit: localAmount + landedCostAmount,
|
||||
debit: totalLocal + landedCostAmount,
|
||||
accountId:
|
||||
['inventory'].indexOf(entry.item.type) !== -1
|
||||
? entry.item.inventoryAccountId
|
||||
|
||||
@@ -77,11 +77,11 @@ export default class VendorCreditGLEntries {
|
||||
index: number
|
||||
): ILedgerEntry => {
|
||||
const commonEntity = this.getVendorCreditGLCommonEntry(vendorCredit);
|
||||
const localAmount = entry.amount * vendorCredit.exchangeRate;
|
||||
const totalLocal = entry.totalExcludingTax * vendorCredit.exchangeRate;
|
||||
|
||||
return {
|
||||
...commonEntity,
|
||||
credit: localAmount,
|
||||
credit: totalLocal,
|
||||
index: index + 2,
|
||||
itemId: entry.itemId,
|
||||
itemQuantity: entry.quantity,
|
||||
|
||||
@@ -13,6 +13,7 @@ export const transformEstimateToPdfTemplate = (
|
||||
description: entry.description,
|
||||
rate: entry.rateFormatted,
|
||||
quantity: entry.quantityFormatted,
|
||||
discount: entry.discountFormatted,
|
||||
total: entry.totalFormatted,
|
||||
})),
|
||||
total: estimate.totalFormatted,
|
||||
@@ -21,6 +22,7 @@ export const transformEstimateToPdfTemplate = (
|
||||
customerNote: estimate.note,
|
||||
termsConditions: estimate.termsConditions,
|
||||
customerAddress: contactAddressTextFormat(estimate.customer),
|
||||
showLineDiscount: estimate.entries.some((entry) => entry.discountFormatted),
|
||||
discount: estimate.discountAmountFormatted,
|
||||
discountLabel: estimate.discountPercentageFormatted
|
||||
? `Discount [${estimate.discountPercentageFormatted}]`
|
||||
|
||||
@@ -154,6 +154,6 @@ export class CommandSaleInvoiceDTOTransformer {
|
||||
* @returns {number}
|
||||
*/
|
||||
private getDueBalanceItemEntries = (entries: ItemEntry[]) => {
|
||||
return sumBy(entries, (e) => e.amount);
|
||||
return sumBy(entries, (e) => e.total);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ export class SaleInvoiceGLEntries {
|
||||
index: number
|
||||
): ILedgerEntry => {
|
||||
const commonEntry = this.getInvoiceGLCommonEntry(saleInvoice);
|
||||
const localAmount = entry.amountExludingTax * saleInvoice.exchangeRate;
|
||||
const localAmount = entry.totalExcludingTax * saleInvoice.exchangeRate;
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IItemEntry } from '@/interfaces';
|
||||
import { DiscountType, IItemEntry } from '@/interfaces';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from '@/utils';
|
||||
|
||||
@@ -8,7 +8,13 @@ export class ItemEntryTransformer extends Transformer {
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['quantityFormatted', 'rateFormatted', 'totalFormatted'];
|
||||
return [
|
||||
'quantityFormatted',
|
||||
'rateFormatted',
|
||||
'totalFormatted',
|
||||
'discountFormatted',
|
||||
'discountAmountFormatted',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -43,4 +49,34 @@ export class ItemEntryTransformer extends Transformer {
|
||||
money: false,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted discount of item entry.
|
||||
* @param {IItemEntry} entry
|
||||
* @returns {string}
|
||||
*/
|
||||
protected discountFormatted = (entry: IItemEntry): string => {
|
||||
if (!entry.discount) {
|
||||
return '';
|
||||
}
|
||||
return entry.discountType === DiscountType.Percentage
|
||||
? `${entry.discount}%`
|
||||
: formatNumber(entry.discount, {
|
||||
currencyCode: this.context.currencyCode,
|
||||
money: false,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted discount amount of item entry.
|
||||
* @param {IItemEntry} entry
|
||||
* @returns {string}
|
||||
*/
|
||||
protected discountAmountFormatted = (entry: IItemEntry): string => {
|
||||
return formatNumber(entry.discountAmount, {
|
||||
currencyCode: this.context.currencyCode,
|
||||
money: false,
|
||||
excerptZero: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,13 +43,14 @@ export const transformInvoiceToPdfTemplate = (
|
||||
description: entry.description,
|
||||
rate: entry.rateFormatted,
|
||||
quantity: entry.quantityFormatted,
|
||||
discount: entry.discountFormatted,
|
||||
total: entry.totalFormatted,
|
||||
})),
|
||||
taxes: invoice.taxes.map((tax) => ({
|
||||
label: tax.name,
|
||||
amount: tax.taxRateAmountFormatted,
|
||||
})),
|
||||
|
||||
showLineDiscount: invoice.entries.some((entry) => entry.discountFormatted),
|
||||
customerAddress: contactAddressTextFormat(invoice.customer),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -143,10 +143,10 @@ export class SaleReceiptGLEntries {
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve receipt income item GL entry.
|
||||
* @param {ISaleReceipt} saleReceipt -
|
||||
* @param {IItemEntry} entry -
|
||||
* @param {number} index -
|
||||
* Retrieve receipt income item G/L entry.
|
||||
* @param {ISaleReceipt} saleReceipt -
|
||||
* @param {IItemEntry} entry -
|
||||
* @param {number} index -
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getReceiptIncomeItemEntry = R.curry(
|
||||
@@ -156,11 +156,11 @@ export class SaleReceiptGLEntries {
|
||||
index: number
|
||||
): ILedgerEntry => {
|
||||
const commonEntry = this.getIncomeGLCommonEntry(saleReceipt);
|
||||
const itemIncome = entry.amount * saleReceipt.exchangeRate;
|
||||
const totalLocal = entry.totalExcludingTax * saleReceipt.exchangeRate;
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
credit: itemIncome,
|
||||
credit: totalLocal,
|
||||
accountId: entry.item.sellAccountId,
|
||||
note: entry.description,
|
||||
index: index + 2,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ISaleReceipt } from '@/interfaces';
|
||||
import { contactAddressTextFormat } from '@/utils/address-text-format';
|
||||
import { ReceiptPaperTemplateProps } from '@bigcapital/pdf-templates';
|
||||
|
||||
export const transformReceiptToBrandingTemplateAttributes = (
|
||||
saleReceipt: ISaleReceipt
|
||||
saleReceipt
|
||||
): Partial<ReceiptPaperTemplateProps> => {
|
||||
return {
|
||||
total: saleReceipt.totalFormatted,
|
||||
@@ -13,6 +12,7 @@ export const transformReceiptToBrandingTemplateAttributes = (
|
||||
description: entry.description,
|
||||
rate: entry.rateFormatted,
|
||||
quantity: entry.quantityFormatted,
|
||||
discount: entry.discountFormatted,
|
||||
total: entry.totalFormatted,
|
||||
})),
|
||||
receiptNumber: saleReceipt.receiptNumber,
|
||||
@@ -21,6 +21,9 @@ export const transformReceiptToBrandingTemplateAttributes = (
|
||||
discountLabel: saleReceipt.discountPercentageFormatted
|
||||
? `Discount [${saleReceipt.discountPercentageFormatted}]`
|
||||
: 'Discount',
|
||||
showLineDiscount: saleReceipt.entries.some(
|
||||
(entry) => entry.discountFormatted
|
||||
),
|
||||
adjustment: saleReceipt.adjustmentFormatted,
|
||||
customerAddress: contactAddressTextFormat(saleReceipt.customer),
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Inject, Service } from 'typedi';
|
||||
import { keyBy, sumBy } from 'lodash';
|
||||
import { ItemEntry } from '@/models';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
import { IItem, IItemEntry, IItemEntryDTO } from '@/interfaces';
|
||||
import { IItemEntry } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class ItemEntriesTaxTransactions {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = function(knex) {
|
||||
return knex.schema.createTable('api_keys', (table) => {
|
||||
table.increments('id').primary();
|
||||
|
||||
table.bigInteger('tenant_id').unsigned().index().references('id').inTable('tenants');
|
||||
table.integer('user_id').unsigned().index().references('id').inTable('users');
|
||||
|
||||
table.string('name');
|
||||
table.text('key');
|
||||
|
||||
table.dateTime('expires_at').nullable();
|
||||
table.dateTime('revoked_at').nullable();
|
||||
table.timestamps();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = function(knex) {
|
||||
return knex.schema.dropTableIfExists('api_keys');
|
||||
};
|
||||
@@ -62,6 +62,9 @@ export function DataTable(props) {
|
||||
initialPageIndex = 0,
|
||||
initialPageSize = 20,
|
||||
|
||||
// Hidden columns.
|
||||
initialHiddenColumns = [],
|
||||
|
||||
updateDebounceTime = 200,
|
||||
selectionColumnWidth = 42,
|
||||
|
||||
@@ -115,6 +118,7 @@ export function DataTable(props) {
|
||||
columnResizing: {
|
||||
columnWidths: initialColumnsWidths || {},
|
||||
},
|
||||
hiddenColumns: initialHiddenColumns,
|
||||
},
|
||||
manualPagination,
|
||||
pageCount: controlledPageCount,
|
||||
|
||||
@@ -20,6 +20,10 @@ export default function BillDetailTable() {
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
initialHiddenColumns={
|
||||
// If any entry has no discount, hide the discount column.
|
||||
entries?.some((e) => e.discount_formatted) ? [] : ['discount']
|
||||
}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -70,6 +70,18 @@ export const useBillReadonlyEntriesTableColumns = () => {
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'discount',
|
||||
Header: 'Discount',
|
||||
accessor: 'discount_formatted',
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
width: getColumnWidth(entries, 'discount_formatted', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'total_formatted',
|
||||
|
||||
@@ -22,6 +22,10 @@ export default function CreditNoteDetailTable() {
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
initialHiddenColumns={
|
||||
// If any entry has no discount, hide the discount column.
|
||||
entries?.some((e) => e.discount_formatted) ? [] : ['discount']
|
||||
}
|
||||
className={'table-constrant'}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
TextOverviewTooltipCell,
|
||||
FormatNumberCell,
|
||||
Choose,
|
||||
} from '@/components';
|
||||
import { useCreditNoteDetailDrawerContext } from './CreditNoteDetailDrawerProvider';
|
||||
@@ -68,6 +67,18 @@ export const useCreditNoteReadOnlyEntriesColumns = () => {
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'discount',
|
||||
Header: 'Discount',
|
||||
accessor: 'discount_formatted',
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
width: getColumnWidth(entries, 'discount_formatted', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'total_formatted',
|
||||
|
||||
@@ -23,6 +23,10 @@ export default function EstimateDetailTable() {
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
initialHiddenColumns={
|
||||
// If any entry has no discount, hide the discount column.
|
||||
entries?.some((e) => e.discount_formatted) ? [] : ['discount']
|
||||
}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -55,6 +55,18 @@ export const useEstimateReadonlyEntriesColumns = () => {
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'discount',
|
||||
Header: 'Discount',
|
||||
accessor: 'discount_formatted',
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
width: getColumnWidth(entries, 'discount_formatted', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'total_formatted',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import { CommercialDocEntriesTable } from '@/components';
|
||||
|
||||
@@ -25,6 +26,10 @@ export default function InvoiceDetailTable() {
|
||||
columns={columns}
|
||||
data={entries}
|
||||
styleName={TableStyle.Constrant}
|
||||
initialHiddenColumns={
|
||||
// If any entry has no discount, hide the discount column.
|
||||
entries?.some((e) => e.discount_formatted) ? [] : ['discount']
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,18 @@ export const useInvoiceReadonlyEntriesColumns = () => {
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'discount',
|
||||
Header: 'Discount',
|
||||
accessor: 'discount_formatted',
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
width: getColumnWidth(entries, 'discount_formatted', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'total_formatted',
|
||||
|
||||
@@ -24,6 +24,10 @@ export default function ReceiptDetailTable() {
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
initialHiddenColumns={
|
||||
// If any entry has no discount, hide the discount column.
|
||||
entries?.some((e) => e.discount_formatted) ? [] : ['discount']
|
||||
}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -50,6 +50,18 @@ export const useReceiptReadonlyEntriesTableColumns = () => {
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'discount',
|
||||
Header: 'Discount',
|
||||
accessor: 'discount_formatted',
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
width: getColumnWidth(entries, 'discount_formatted', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'amount',
|
||||
|
||||
@@ -23,6 +23,10 @@ export default function VendorCreditDetailTable() {
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
initialHiddenColumns={
|
||||
// If any entry has no discount, hide the discount column.
|
||||
entries?.some((e) => e.discount_formatted) ? [] : ['discount']
|
||||
}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
TextOverviewTooltipCell,
|
||||
FormatNumberCell,
|
||||
Choose,
|
||||
} from '@/components';
|
||||
import { useVendorCreditDetailDrawerContext } from './VendorCreditDetailDrawerProvider';
|
||||
@@ -69,6 +68,18 @@ export const useVendorCreditReadonlyEntriesTableColumns = () => {
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'discount',
|
||||
Header: 'Discount',
|
||||
accessor: 'discount_formatted',
|
||||
width: getColumnWidth(entries, 'discount_formatted', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'total_formatted',
|
||||
|
||||
@@ -92,6 +92,10 @@ export interface EstimatePaperTemplateProps extends PaperTemplateProps {
|
||||
lineQuantityLabel?: string;
|
||||
lineRateLabel?: string;
|
||||
lineTotalLabel?: string;
|
||||
|
||||
// # Line Discount
|
||||
lineDiscountLabel?: string;
|
||||
showLineDiscount?: boolean;
|
||||
}
|
||||
|
||||
export function EstimatePaperTemplate({
|
||||
@@ -173,8 +177,11 @@ export function EstimatePaperTemplate({
|
||||
lineQuantityLabel = 'Qty',
|
||||
lineRateLabel = 'Rate',
|
||||
lineTotalLabel = 'Total',
|
||||
}: EstimatePaperTemplateProps) {
|
||||
|
||||
// # Line Discount
|
||||
lineDiscountLabel = 'Discount',
|
||||
showLineDiscount = false,
|
||||
}: EstimatePaperTemplateProps) {
|
||||
return (
|
||||
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
||||
<Stack spacing={24}>
|
||||
@@ -240,6 +247,12 @@ export function EstimatePaperTemplate({
|
||||
},
|
||||
{ label: lineQuantityLabel, accessor: 'quantity' },
|
||||
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||
{
|
||||
label: lineDiscountLabel,
|
||||
accessor: 'discount',
|
||||
align: 'right',
|
||||
visible: showLineDiscount,
|
||||
},
|
||||
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||
]}
|
||||
data={lines}
|
||||
|
||||
@@ -17,15 +17,16 @@ import {
|
||||
DefaultPdfTemplateAddressBilledFrom,
|
||||
} from './_constants';
|
||||
|
||||
interface PapaerLine {
|
||||
interface InvoiceLine {
|
||||
item?: string;
|
||||
description?: string;
|
||||
quantity?: string;
|
||||
rate?: string;
|
||||
total?: string;
|
||||
discount?: string;
|
||||
}
|
||||
|
||||
interface PaperTax {
|
||||
interface InvoiceTaxLine {
|
||||
label: string;
|
||||
amount: string;
|
||||
}
|
||||
@@ -71,6 +72,10 @@ export interface InvoicePaperTemplateProps extends PaperTemplateProps {
|
||||
lineRateLabel?: string;
|
||||
lineTotalLabel?: string;
|
||||
|
||||
// # Line Discount
|
||||
lineDiscountLabel?: string;
|
||||
showLineDiscount?: boolean;
|
||||
|
||||
// Total
|
||||
showTotal?: boolean;
|
||||
totalLabel?: string;
|
||||
@@ -113,8 +118,8 @@ export interface InvoicePaperTemplateProps extends PaperTemplateProps {
|
||||
showStatement?: boolean;
|
||||
statement?: string;
|
||||
|
||||
lines?: Array<PapaerLine>;
|
||||
taxes?: Array<PaperTax>;
|
||||
lines?: Array<InvoiceLine>;
|
||||
taxes?: Array<InvoiceTaxLine>;
|
||||
}
|
||||
|
||||
export function InvoicePaperTemplate({
|
||||
@@ -165,6 +170,10 @@ export function InvoicePaperTemplate({
|
||||
paymentMadeLabel = 'Payment Made',
|
||||
dueAmountLabel = 'Balance Due',
|
||||
|
||||
// # Line Discount
|
||||
lineDiscountLabel = 'Discount',
|
||||
showLineDiscount = false,
|
||||
|
||||
// Totals
|
||||
showTotal = true,
|
||||
total = '$662.75',
|
||||
@@ -277,6 +286,12 @@ export function InvoicePaperTemplate({
|
||||
align: 'right',
|
||||
},
|
||||
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||
{
|
||||
label: lineDiscountLabel,
|
||||
accessor: 'discount',
|
||||
align: 'right',
|
||||
visible: showLineDiscount,
|
||||
},
|
||||
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||
]}
|
||||
data={lines}
|
||||
|
||||
@@ -90,11 +90,14 @@ interface PaperTemplateTableProps {
|
||||
value?: JSX.Element;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
thStyle?: React.CSSProperties;
|
||||
visible?: boolean;
|
||||
}>;
|
||||
data: Array<Record<string, any>>;
|
||||
}
|
||||
|
||||
PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
||||
const filteredColumns = columns.filter((col) => col.visible !== false);
|
||||
|
||||
return (
|
||||
<table
|
||||
className={css`
|
||||
@@ -140,7 +143,7 @@ PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((col, index) => (
|
||||
{filteredColumns.map((col, index) => (
|
||||
<x.th key={index} textAlign={col.align} style={col.thStyle}>
|
||||
{col.label}
|
||||
</x.th>
|
||||
@@ -151,7 +154,7 @@ PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
||||
<tbody>
|
||||
{data.map((_data: any) => (
|
||||
<tr>
|
||||
{columns.map((column, index) => (
|
||||
{filteredColumns.map((column, index) => (
|
||||
<x.td textAlign={column.align} key={index}>
|
||||
{isFunction(column?.accessor)
|
||||
? column?.accessor(_data)
|
||||
|
||||
@@ -71,9 +71,14 @@ export interface ReceiptPaperTemplateProps extends PaperTemplateProps {
|
||||
description: string;
|
||||
rate: string;
|
||||
quantity: string;
|
||||
discount?: string;
|
||||
total: string;
|
||||
}>;
|
||||
|
||||
// # Line Discount
|
||||
lineDiscountLabel?: string;
|
||||
showLineDiscount?: boolean;
|
||||
|
||||
// Receipt Date.
|
||||
receiptDateLabel?: string;
|
||||
showReceiptDate?: boolean;
|
||||
@@ -165,6 +170,10 @@ export function ReceiptPaperTemplate({
|
||||
lineQuantityLabel = 'Qty',
|
||||
lineRateLabel = 'Rate',
|
||||
lineTotalLabel = 'Total',
|
||||
|
||||
// # Line Discount
|
||||
lineDiscountLabel = 'Discount',
|
||||
showLineDiscount = false,
|
||||
}: ReceiptPaperTemplateProps) {
|
||||
return (
|
||||
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}>
|
||||
@@ -223,9 +232,16 @@ export function ReceiptPaperTemplate({
|
||||
</Text>
|
||||
</Stack>
|
||||
),
|
||||
thStyle: { width: '60%' },
|
||||
},
|
||||
{ label: lineQuantityLabel, accessor: 'quantity' },
|
||||
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||
{
|
||||
label: lineDiscountLabel,
|
||||
accessor: 'discount',
|
||||
align: 'right',
|
||||
visible: showLineDiscount,
|
||||
},
|
||||
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||
]}
|
||||
data={lines}
|
||||
|
||||
Reference in New Issue
Block a user