mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 23:00:34 +00:00
feat: allow quantity of entries accept decimal value (#753)
This commit is contained in:
@@ -121,7 +121,7 @@ export default class BillsController extends BaseController {
|
|||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount')
|
check('entries.*.discount')
|
||||||
.optional({ nullable: true })
|
.optional({ nullable: true })
|
||||||
.isNumeric()
|
.isNumeric()
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export default class VendorCreditController extends BaseController {
|
|||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount')
|
check('entries.*.discount')
|
||||||
.optional({ nullable: true })
|
.optional({ nullable: true })
|
||||||
.isNumeric()
|
.isNumeric()
|
||||||
@@ -209,7 +209,7 @@ export default class VendorCreditController extends BaseController {
|
|||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount')
|
check('entries.*.discount')
|
||||||
.optional({ nullable: true })
|
.optional({ nullable: true })
|
||||||
.isNumeric()
|
.isNumeric()
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount')
|
check('entries.*.discount')
|
||||||
.optional({ nullable: true })
|
.optional({ nullable: true })
|
||||||
.isNumeric()
|
.isNumeric()
|
||||||
@@ -755,9 +755,8 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await this.getCreditNoteStateService.getCreditNoteState(
|
const data =
|
||||||
tenantId
|
await this.getCreditNoteStateService.getCreditNoteState(tenantId);
|
||||||
);
|
|
||||||
return res.status(200).send({ data });
|
return res.status(200).send({ data });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
check('entries').exists().isArray({ min: 1 }),
|
check('entries').exists().isArray({ min: 1 }),
|
||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.description').optional({ nullable: true }).trim(),
|
check('entries.*.description').optional({ nullable: true }).trim(),
|
||||||
check('entries.*.discount')
|
check('entries.*.discount')
|
||||||
@@ -562,9 +562,8 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await this.saleEstimatesApplication.getSaleEstimateState(
|
const data =
|
||||||
tenantId
|
await this.saleEstimatesApplication.getSaleEstimateState(tenantId);
|
||||||
);
|
|
||||||
return res.status(200).send({ data });
|
return res.status(200).send({ data });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export default class SalesReceiptsController extends BaseController {
|
|||||||
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
|
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
|
||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount')
|
check('entries.*.discount')
|
||||||
.optional({ nullable: true })
|
.optional({ nullable: true })
|
||||||
@@ -392,9 +392,8 @@ export default class SalesReceiptsController extends BaseController {
|
|||||||
|
|
||||||
// Retrieves receipt in pdf format.
|
// Retrieves receipt in pdf format.
|
||||||
try {
|
try {
|
||||||
const data = await this.saleReceiptsApplication.getSaleReceiptState(
|
const data =
|
||||||
tenantId
|
await this.saleReceiptsApplication.getSaleReceiptState(tenantId);
|
||||||
);
|
|
||||||
return res.status(200).send({ data });
|
return res.status(200).send({ data });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.up = function (knex) {
|
||||||
|
return knex.schema
|
||||||
|
.table('items_entries', (table) => {
|
||||||
|
table.decimal('quantity', 13, 3).alter();
|
||||||
|
})
|
||||||
|
.table('inventory_transactions', (table) => {
|
||||||
|
table.decimal('quantity', 13, 3).alter();
|
||||||
|
})
|
||||||
|
.table('inventory_cost_lot_tracker', (table) => {
|
||||||
|
table.decimal('quantity', 13, 3).alter();
|
||||||
|
})
|
||||||
|
.table('items', (table) => {
|
||||||
|
table.decimal('quantityOnHand', 13, 3).alter();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param { import("knex").Knex } knex
|
||||||
|
* @returns { Promise<void> }
|
||||||
|
*/
|
||||||
|
exports.down = function (knex) {
|
||||||
|
return knex.schema
|
||||||
|
.table('items_entries', (table) => {
|
||||||
|
table.integer('quantity').alter();
|
||||||
|
})
|
||||||
|
.table('inventory_transactions', (table) => {
|
||||||
|
table.integer('quantity').alter();
|
||||||
|
})
|
||||||
|
.table('inventory_cost_lot_tracker', (table) => {
|
||||||
|
table.integer('quantity').alter();
|
||||||
|
})
|
||||||
|
.table('items', (table) => {
|
||||||
|
table.integer('quantityOnHand').alter();
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { FormGroup, NumericInput, Intent } from '@blueprintjs/core';
|
import { FormGroup, NumericInput, Intent } from '@blueprintjs/core';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { CellType } from '@/constants';
|
import { CellType } from '@/constants';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
|
|
||||||
@@ -12,37 +10,44 @@ import { CLASSES } from '@/constants/classes';
|
|||||||
export default function NumericInputCell({
|
export default function NumericInputCell({
|
||||||
row: { index },
|
row: { index },
|
||||||
column: { id },
|
column: { id },
|
||||||
cell: { value: initialValue },
|
cell: { value: controlledInputValue },
|
||||||
payload,
|
payload,
|
||||||
}) {
|
}: any) {
|
||||||
const [value, setValue] = useState(initialValue);
|
const [valueAsNumber, setValueAsNumber] = useState<number | null>(
|
||||||
|
controlledInputValue || null,
|
||||||
|
);
|
||||||
|
const handleInputValueChange = (
|
||||||
|
valueAsNumber: number,
|
||||||
|
valueAsString: string,
|
||||||
|
) => {
|
||||||
|
setValueAsNumber(valueAsNumber);
|
||||||
|
};
|
||||||
|
const handleInputBlur = () => {
|
||||||
|
payload.updateData(index, id, valueAsNumber);
|
||||||
|
};
|
||||||
|
|
||||||
const handleValueChange = (newValue) => {
|
|
||||||
setValue(newValue);
|
|
||||||
};
|
|
||||||
const onBlur = () => {
|
|
||||||
payload.updateData(index, id, value);
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(initialValue);
|
setValueAsNumber(controlledInputValue);
|
||||||
}, [initialValue]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [controlledInputValue]);
|
||||||
|
|
||||||
const error = payload.errors?.[index]?.[id];
|
const error = payload.errors?.[index]?.[id];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
intent={error ? Intent.DANGER : null}
|
intent={error ? Intent.DANGER : undefined}
|
||||||
className={classNames(CLASSES.FILL)}
|
className={classNames(CLASSES.FILL)}
|
||||||
>
|
>
|
||||||
<NumericInput
|
<NumericInput
|
||||||
value={value}
|
asyncControl
|
||||||
onValueChange={handleValueChange}
|
value={controlledInputValue}
|
||||||
onBlur={onBlur}
|
onValueChange={handleInputValueChange}
|
||||||
fill={true}
|
onBlur={handleInputBlur}
|
||||||
buttonPosition={'none'}
|
buttonPosition={'none'}
|
||||||
|
fill
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
NumericInputCell.cellType = CellType.Field;
|
NumericInputCell.cellType = CellType.Field;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import {
|
import {
|
||||||
FormatNumberCell,
|
|
||||||
TextOverviewTooltipCell,
|
TextOverviewTooltipCell,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
Choose,
|
Choose,
|
||||||
@@ -51,9 +50,8 @@ export const useBillReadonlyEntriesTableColumns = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: intl.get('quantity'),
|
Header: intl.get('quantity'),
|
||||||
accessor: 'quantity',
|
accessor: 'quantity_formatted',
|
||||||
Cell: FormatNumberCell,
|
width: getColumnWidth(entries, 'quantity_formatted', {
|
||||||
width: getColumnWidth(entries, 'quantity', {
|
|
||||||
minWidth: 60,
|
minWidth: 60,
|
||||||
magicSpacing: 5,
|
magicSpacing: 5,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -48,9 +48,8 @@ export const useCreditNoteReadOnlyEntriesColumns = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: intl.get('quantity'),
|
Header: intl.get('quantity'),
|
||||||
accessor: 'quantity',
|
accessor: 'quantity_formatted',
|
||||||
Cell: FormatNumberCell,
|
width: getColumnWidth(entries, 'quantity_formatted', {
|
||||||
width: getColumnWidth(entries, 'quantity', {
|
|
||||||
minWidth: 60,
|
minWidth: 60,
|
||||||
magicSpacing: 5,
|
magicSpacing: 5,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -54,11 +54,10 @@ export const useInvoiceReadonlyEntriesColumns = () => {
|
|||||||
{
|
{
|
||||||
Header: intl.get('quantity'),
|
Header: intl.get('quantity'),
|
||||||
accessor: 'quantity',
|
accessor: 'quantity',
|
||||||
Cell: FormatNumberCell,
|
|
||||||
align: 'right',
|
align: 'right',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
textOverview: true,
|
textOverview: true,
|
||||||
width: getColumnWidth(entries, 'quantity', {
|
width: getColumnWidth(entries, 'quantity_formatted', {
|
||||||
minWidth: 60,
|
minWidth: 60,
|
||||||
magicSpacing: 5,
|
magicSpacing: 5,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const useEstimateTransactionsColumns = () => {
|
|||||||
{
|
{
|
||||||
id: 'qunatity',
|
id: 'qunatity',
|
||||||
Header: intl.get('item.drawer_quantity_sold'),
|
Header: intl.get('item.drawer_quantity_sold'),
|
||||||
accessor: 'quantity',
|
accessor: 'quantity_formatted',
|
||||||
align: 'right',
|
align: 'right',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,9 +31,8 @@ export const useReceiptReadonlyEntriesTableColumns = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: intl.get('quantity'),
|
Header: intl.get('quantity'),
|
||||||
accessor: 'quantity',
|
accessor: 'quantity_formatted',
|
||||||
Cell: FormatNumberCell,
|
width: getColumnWidth(entries, 'quantity_formatted', {
|
||||||
width: getColumnWidth(entries, 'quantity', {
|
|
||||||
minWidth: 60,
|
minWidth: 60,
|
||||||
magicSpacing: 5,
|
magicSpacing: 5,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user