Merge pull request #881 from bigcapitalhq/bugs-bashing

bugs bashing
This commit is contained in:
Ahmed Bouhuolia
2025-12-29 22:08:56 +02:00
committed by GitHub
39 changed files with 372 additions and 528 deletions

View File

@@ -211,7 +211,7 @@ export class InventoryValuationSheet extends FinancialSheet {
* Detarmines whether the items post filter is active. * Detarmines whether the items post filter is active.
*/ */
private isItemsPostFilter = (): boolean => { private isItemsPostFilter = (): boolean => {
return isEmpty(this.query.itemsIds); return !isEmpty(this.query.itemsIds);
}; };
/** /**

View File

@@ -18,7 +18,7 @@ export class InventoryValuationSheetService {
private readonly inventoryValuationMeta: InventoryValuationMetaInjectable, private readonly inventoryValuationMeta: InventoryValuationMetaInjectable,
private readonly eventPublisher: EventEmitter2, private readonly eventPublisher: EventEmitter2,
private readonly inventoryValuationSheetRepository: InventoryValuationSheetRepository, private readonly inventoryValuationSheetRepository: InventoryValuationSheetRepository,
) {} ) { }
/** /**
* Inventory valuation sheet. * Inventory valuation sheet.

View File

@@ -172,7 +172,10 @@ export class TrialBalanceSheet extends FinancialSheet {
private filterNoneTransactions = ( private filterNoneTransactions = (
accountNode: ITrialBalanceAccount accountNode: ITrialBalanceAccount
): boolean => { ): boolean => {
return false === this.repository.totalAccountsLedger.isEmpty(); const accountLedger = this.repository.totalAccountsLedger.whereAccountId(
accountNode.id,
);
return !accountLedger.isEmpty();
}; };
/** /**

View File

@@ -93,7 +93,7 @@ export class InventoryComputeCostService {
*/ */
async scheduleComputeItemCost(itemId: number, startingDate: Date | string) { async scheduleComputeItemCost(itemId: number, startingDate: Date | string) {
const debounceKey = `inventory-cost-compute-debounce:${itemId}`; const debounceKey = `inventory-cost-compute-debounce:${itemId}`;
const debounceTime = 1000 * 60; // 1 minute const debounceTime = 1000 * 10; // 10 seconds
// Generate a unique job ID or use a custom identifier // Generate a unique job ID or use a custom identifier
const jobId = `task-${Date.now()}-${Math.random().toString(36).substring(2)}`; const jobId = `task-${Date.now()}-${Math.random().toString(36).substring(2)}`;

View File

@@ -2,7 +2,8 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { Processor, WorkerHost } from '@nestjs/bullmq'; import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common'; import { Scope } from '@nestjs/common';
import { Job } from 'bullmq'; import { Job } from 'bullmq';
import { ClsService } from 'nestjs-cls'; import { ClsService, UseCls } from 'nestjs-cls';
import * as moment from 'moment';
import { TenantJobPayload } from '@/interfaces/Tenant'; import { TenantJobPayload } from '@/interfaces/Tenant';
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service'; import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
@@ -14,7 +15,7 @@ import { Process } from '@nestjs/bull';
interface ComputeItemCostJobPayload extends TenantJobPayload { interface ComputeItemCostJobPayload extends TenantJobPayload {
itemId: number; itemId: number;
startingDate: Date; startingDate: Date | string;
} }
@Processor({ @Processor({
name: ComputeItemCostQueue, name: ComputeItemCostQueue,
@@ -39,28 +40,34 @@ export class ComputeItemCostProcessor extends WorkerHost {
* @param {Job<ComputeItemCostJobPayload>} job - The job to process * @param {Job<ComputeItemCostJobPayload>} job - The job to process
*/ */
@Process(ComputeItemCostQueueJob) @Process(ComputeItemCostQueueJob)
@UseCls()
async process(job: Job<ComputeItemCostJobPayload>) { async process(job: Job<ComputeItemCostJobPayload>) {
const { itemId, startingDate, organizationId, userId } = job.data; const { itemId, startingDate, organizationId, userId } = job.data;
console.log(`Compute item cost for item ${itemId} started`); // Parse startingDate using moment to handle both Date and string formats
const startingDateObj = moment(startingDate).toDate();
console.log(`[info] Compute item cost for item ${itemId} started`, {
payload: job.data,
jobId: job.id
});
this.clsService.set('organizationId', organizationId); this.clsService.set('organizationId', organizationId);
this.clsService.set('userId', userId); this.clsService.set('userId', userId);
try { try {
await this.inventoryComputeCostService.computeItemCost( await this.inventoryComputeCostService.computeItemCost(
startingDate, startingDateObj,
itemId, itemId,
); );
// Emit job completed event // Emit job completed event
await this.eventEmitter.emitAsync( await this.eventEmitter.emitAsync(
events.inventory.onComputeItemCostJobCompleted, events.inventory.onComputeItemCostJobCompleted,
{ startingDate, itemId, organizationId, userId }, { startingDate: startingDateObj, itemId, organizationId, userId },
); );
console.log(`[info] Compute item cost for item ${itemId} completed successfully`);
console.log(`Compute item cost for item ${itemId} completed`);
} catch (error) { } catch (error) {
console.error('Error computing item cost:', error); console.error(`[error] Error computing item cost for item ${itemId}:`, error);
console.error('Error stack:', error instanceof Error ? error.stack : 'No stack trace');
throw error; throw error;
} }
} }

View File

@@ -19,7 +19,7 @@ export class SaleInvoiceCostGLEntries {
private readonly inventoryCostLotTracker: TenantModelProxy< private readonly inventoryCostLotTracker: TenantModelProxy<
typeof InventoryCostLotTracker typeof InventoryCostLotTracker
>, >,
) {} ) { }
/** /**
* Writes journal entries from sales invoices. * Writes journal entries from sales invoices.

View File

@@ -10,7 +10,7 @@ import { events } from '@/common/events/events';
@Injectable() @Injectable()
export class InvoiceGLEntriesSubscriber { export class InvoiceGLEntriesSubscriber {
constructor(public readonly saleInvoiceGLEntries: SaleInvoiceGLEntries) {} constructor(public readonly saleInvoiceGLEntries: SaleInvoiceGLEntries) { }
/** /**
* Records journal entries of the non-inventory invoice. * Records journal entries of the non-inventory invoice.

View File

@@ -26,7 +26,7 @@ export class TransactionsLockingService {
constructor( constructor(
private readonly transactionsLockingRepo: TransactionsLockingRepository, private readonly transactionsLockingRepo: TransactionsLockingRepository,
private readonly eventPublisher: EventEmitter2, private readonly eventPublisher: EventEmitter2,
) {} ) { }
/** /**
* Enable/disable all transacations locking. * Enable/disable all transacations locking.

View File

@@ -20,12 +20,3 @@ export function BranchSelect({ branches, ...rest }) {
/> />
); );
} }
/**
*
* @param {*} param0
* @returns
*/
export function BranchSelectButton({ label, ...rest }) {
return <Button text={label} {...rest} />;
}

View File

@@ -171,6 +171,15 @@ export const financialReportMenus = [
subject: AbilitySubject.Report, subject: AbilitySubject.Report,
ability: ReportsAction.READ_INVENTORY_ITEM_DETAILS, ability: ReportsAction.READ_INVENTORY_ITEM_DETAILS,
}, },
{
title: <T id={'inventory_valuation'} />,
desc: (
<T id={'summerize_your_transactions_for_each_inventory_item'} />
),
link: '/financial-reports/inventory-valuation',
subject: AbilitySubject.Report,
ability: ReportsAction.READ_INVENTORY_VALUATION_SUMMARY,
},
], ],
}, },
{ {

View File

@@ -1,7 +1,7 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { FastField, Field, ErrorMessage } from 'formik'; import { ErrorMessage, useFormikContext } from 'formik';
import { import {
Classes, Classes,
FormGroup, FormGroup,
@@ -10,15 +10,14 @@ import {
InputGroup, InputGroup,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import classNames from 'classnames'; import classNames from 'classnames';
import { FormattedMessage as T, If } from '@/components'; import { FormattedMessage as T, If, FFormGroup, FSelect, FRadioGroup, FInputGroup } from '@/components';
import { inputIntent, handleStringChange } from '@/utils'; import { handleStringChange } from '@/utils';
import { FieldRequiredHint, ListSelect } from '@/components'; import { FieldRequiredHint } from '@/components';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import allocateLandedCostType from '@/constants/allocateLandedCostType'; import allocateLandedCostType from '@/constants/allocateLandedCostType';
import AllocateLandedCostFormBody from './AllocateLandedCostFormBody'; import AllocateLandedCostFormBody from './AllocateLandedCostFormBody';
import { import {
transactionsSelectShouldUpdate,
allocateCostToEntries, allocateCostToEntries,
resetAllocatedCostEntries, resetAllocatedCostEntries,
} from './utils'; } from './utils';
@@ -32,196 +31,157 @@ export default function AllocateLandedCostFormFields() {
const { costTransactionEntries, landedCostTransactions } = const { costTransactionEntries, landedCostTransactions } =
useAllocateLandedConstDialogContext(); useAllocateLandedConstDialogContext();
const { values, setFieldValue, form } = useFormikContext();
// Handle transaction type select change.
const handleTransactionTypeChange = (type) => {
const { items } = values;
setFieldValue('transaction_type', type.value);
setFieldValue('transaction_id', '');
setFieldValue('transaction_entry_id', '');
setFieldValue('amount', '');
setFieldValue('items', resetAllocatedCostEntries(items));
};
// Handle transaction select change.
const handleTransactionChange = (transaction) => {
const { items } = values;
setFieldValue('transaction_id', transaction.id);
setFieldValue('transaction_entry_id', '');
setFieldValue('amount', '');
setFieldValue('items', resetAllocatedCostEntries(items));
};
// Handle transaction entry select change.
const handleTransactionEntryChange = (entry) => {
const { id, unallocated_cost_amount: unallocatedAmount } = entry;
const { items, allocation_method } = values;
setFieldValue('amount', unallocatedAmount);
setFieldValue('transaction_entry_id', id);
setFieldValue(
'items',
allocateCostToEntries(unallocatedAmount, allocation_method, items),
);
};
return ( return (
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
{/*------------Transaction type -----------*/} {/*------------Transaction type -----------*/}
<FastField <FFormGroup
name={'transaction_type'} name={'transaction_type'}
transactions={allocateLandedCostType} label={<T id={'transaction_type'} />}
shouldUpdate={transactionsSelectShouldUpdate} labelInfo={<FieldRequiredHint />}
inline
fill
fastField
> >
{({ <FSelect
form: { values, setFieldValue }, name={'transaction_type'}
field: { value }, items={allocateLandedCostType}
meta: { error, touched }, onItemChange={handleTransactionTypeChange}
}) => ( filterable={false}
<FormGroup valueAccessor={'value'}
label={<T id={'transaction_type'} />} textAccessor={'name'}
labelInfo={<FieldRequiredHint />} popoverProps={{ minimal: true }}
helperText={<ErrorMessage name="transaction_type" />} fastField
intent={inputIntent({ error, touched })} />
inline={true} </FFormGroup>
className={classNames(CLASSES.FILL, 'form-group--transaction_type')}
>
<ListSelect
items={allocateLandedCostType}
onItemSelect={(type) => {
const { items } = values;
setFieldValue('transaction_type', type.value);
setFieldValue('transaction_id', '');
setFieldValue('transaction_entry_id', '');
setFieldValue('amount', '');
setFieldValue('items', resetAllocatedCostEntries(items));
}}
filterable={false}
selectedItem={value}
selectedItemProp={'value'}
textProp={'name'}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</FastField>
{/*------------ Transaction -----------*/} {/*------------ Transaction -----------*/}
<Field <FFormGroup
name={'transaction_id'} name={'transaction_id'}
transactions={landedCostTransactions} label={<T id={'transaction_id'} />}
shouldUpdate={transactionsSelectShouldUpdate} labelInfo={<FieldRequiredHint />}
inline
fill
> >
{({ form, field: { value }, meta: { error, touched } }) => ( <FSelect
<FormGroup name={'transaction_id'}
label={<T id={'transaction_id'} />} items={landedCostTransactions}
labelInfo={<FieldRequiredHint />} onItemChange={handleTransactionChange}
intent={inputIntent({ error, touched })} filterable={false}
helperText={<ErrorMessage name="transaction_id" />} valueAccessor={'id'}
className={classNames(CLASSES.FILL, 'form-group--transaction_id')} textAccessor={'name'}
inline={true} labelAccessor={'formatted_unallocated_cost_amount'}
> placeholder={intl.get('landed_cost.dialog.label_select_transaction')}
<ListSelect popoverProps={{ minimal: true }}
items={landedCostTransactions} />
onItemSelect={({ id }) => { </FFormGroup>
const { items } = form.values;
form.setFieldValue('transaction_id', id);
form.setFieldValue('transaction_entry_id', '');
form.setFieldValue('amount', '');
form.setFieldValue('items', resetAllocatedCostEntries(items));
}}
filterable={false}
selectedItem={value}
selectedItemProp={'id'}
textProp={'name'}
labelProp={'formatted_unallocated_cost_amount'}
defaultText={intl.get(
'landed_cost.dialog.label_select_transaction',
)}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</Field>
{/*------------ Transaction line -----------*/} {/*------------ Transaction line -----------*/}
<If condition={costTransactionEntries.length > 0}> <If condition={costTransactionEntries.length > 0}>
<Field <FFormGroup
name={'transaction_entry_id'} name={'transaction_entry_id'}
transactions={costTransactionEntries} label={<T id={'transaction_line'} />}
shouldUpdate={transactionsSelectShouldUpdate} inline
fill
fastField
> >
{({ form, field: { value }, meta: { error, touched } }) => ( <FSelect
<FormGroup name={'transaction_entry_id'}
label={<T id={'transaction_line'} />} items={costTransactionEntries}
intent={inputIntent({ error, touched })} onItemChange={handleTransactionEntryChange}
helperText={<ErrorMessage name="transaction_entry_id" />} filterable={false}
className={classNames( valueAccessor={'id'}
CLASSES.FILL, textAccessor={'name'}
'form-group--transaction_entry_id', labelAccessor={'formatted_unallocated_cost_amount'}
)} placeholder={intl.get(
inline={true} 'landed_cost.dialog.label_select_transaction_entry',
> )}
<ListSelect popoverProps={{ minimal: true }}
items={costTransactionEntries} fastField
onItemSelect={(entry) => { />
const { id, unallocated_cost_amount: unallocatedAmount } = </FFormGroup>
entry;
const { items, allocation_method } = form.values;
form.setFieldValue('amount', unallocatedAmount);
form.setFieldValue('transaction_entry_id', id);
form.setFieldValue(
'items',
allocateCostToEntries(
unallocatedAmount,
allocation_method,
items,
),
);
}}
filterable={false}
selectedItem={value}
selectedItemProp={'id'}
textProp={'name'}
labelProp={'formatted_unallocated_cost_amount'}
defaultText={intl.get(
'landed_cost.dialog.label_select_transaction_entry',
)}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</Field>
</If> </If>
{/*------------ Amount -----------*/} {/*------------ Amount -----------*/}
<FastField name={'amount'}> <FFormGroup
{({ form, field, meta: { error, touched } }) => ( name={'amount'}
<FormGroup label={<T id={'amount'} />}
label={<T id={'amount'} />} inline={true}
intent={inputIntent({ error, touched })} fastField
helperText={<ErrorMessage name="amount" />} >
className={'form-group--amount'} <FInputGroup
inline={true} name={'amount'}
> onBlur={(e) => {
<InputGroup const amount = e.target.value;
{...field} const { allocation_method, items } = values;
onBlur={(e) => {
const amount = e.target.value;
const { allocation_method, items } = form.values;
form.setFieldValue( setFieldValue(
'items', 'items',
allocateCostToEntries(amount, allocation_method, items), allocateCostToEntries(amount, allocation_method, items),
); );
}} }}
/> />
</FormGroup> </FFormGroup>
)}
</FastField>
{/*------------ Allocation method -----------*/} {/*------------ Allocation method -----------*/}
<Field name={'allocation_method'}> <FFormGroup
{({ form, field: { value }, meta: { touched, error } }) => ( name={'allocation_method'}
<FormGroup label={<T id={'allocation_method'} />}
medium={true} medium
label={<T id={'allocation_method'} />} inline
labelInfo={<FieldRequiredHint />} fastField
className={'form-group--allocation_method'} >
intent={inputIntent({ error, touched })} <FRadioGroup
helperText={<ErrorMessage name="allocation_method" />} name={'allocation_method'}
inline={true} onChange={handleStringChange((_value) => {
> const { amount, items } = values;
<RadioGroup
onChange={handleStringChange((_value) => {
const { amount, items } = form.values;
form.setFieldValue('allocation_method', _value); setFieldValue('allocation_method', _value);
form.setFieldValue( setFieldValue(
'items', 'items',
allocateCostToEntries(amount, _value, items), allocateCostToEntries(amount, _value, items),
); );
})} })}
selectedValue={value} inline={true}
inline={true} >
> <Radio label={<T id={'quantity'} />} value="quantity" />
<Radio label={<T id={'quantity'} />} value="quantity" /> <Radio label={<T id={'valuation'} />} value="value" />
<Radio label={<T id={'valuation'} />} value="value" /> </FRadioGroup>
</RadioGroup> </FFormGroup>
</FormGroup>
)}
</Field>
{/*------------ Allocate Landed cost Table -----------*/} {/*------------ Allocate Landed cost Table -----------*/}
<AllocateLandedCostFormBody /> <AllocateLandedCostFormBody />

View File

@@ -2,10 +2,9 @@
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import * as Yup from 'yup'; import * as Yup from 'yup';
import { Formik, Form, Field, ErrorMessage } from 'formik'; import { Formik, Form } from 'formik';
import { inputIntent } from '@/utils'; import { FFormGroup, FSelect, FieldRequiredHint } from '@/components';
import { ListSelect, FieldRequiredHint } from '@/components'; import { Button, Intent, Classes } from '@blueprintjs/core';
import { Button, FormGroup, Intent, Classes } from '@blueprintjs/core';
import { FormattedMessage as T } from '@/components'; import { FormattedMessage as T } from '@/components';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { useContactDuplicateFromContext } from './ContactDuplicateProvider'; import { useContactDuplicateFromContext } from './ContactDuplicateProvider';
@@ -60,29 +59,22 @@ function ContactDuplicateForm({
</p> </p>
{/*------------ Contact Type -----------*/} {/*------------ Contact Type -----------*/}
<Field name={'contact_type'}> <FFormGroup
{({ form, meta: { error, touched } }) => ( name={'contact_type'}
<FormGroup label={<T id={'contact_type'} />}
label={<T id={'contact_type'} />} labelInfo={<FieldRequiredHint />}
labelInfo={<FieldRequiredHint />} className={'form-group--select-list'}
intent={inputIntent({ error, touched })} >
className={'form-group--select-list'} <FSelect
helperText={<ErrorMessage name="contact_type" />} name={'contact_type'}
> items={Contacts}
<ListSelect placeholder={<T id={'select_contact'} />}
items={Contacts} textAccessor={'name'}
onItemSelect={({ path }) => valueAccessor={'path'}
form.setFieldValue('contact_type', path) filterable={false}
} popoverProps={{ minimal: true }}
defaultText={<T id={'select_contact'} />} />
textProp={'name'} </FFormGroup>
selectedItemProp={'name'}
filterable={false}
popoverProps={{ minimal: true }}
/>
</FormGroup>
)}
</Field>
</div> </div>
<div className={Classes.DIALOG_FOOTER}> <div className={Classes.DIALOG_FOOTER}>

View File

@@ -3,7 +3,7 @@ import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import styled from 'styled-components'; import styled from 'styled-components';
import classNames from 'classnames'; import classNames from 'classnames';
import { FastField, ErrorMessage, Field } from 'formik'; import { useFormikContext } from 'formik';
import { Classes, FormGroup, Position } from '@blueprintjs/core'; import { Classes, FormGroup, Position } from '@blueprintjs/core';
import { import {
FFormGroup, FFormGroup,
@@ -11,20 +11,19 @@ import {
FDateInput, FDateInput,
FInputGroup, FInputGroup,
FTextArea, FTextArea,
FSelect,
} from '@/components'; } from '@/components';
import { useAutofocus } from '@/hooks'; import { useAutofocus } from '@/hooks';
import { import {
ListSelect,
FieldRequiredHint, FieldRequiredHint,
Col, Col,
Row, Row,
FeatureCan, FeatureCan,
BranchSelect, BranchSelect,
WarehouseSelect, WarehouseSelect,
BranchSelectButton,
FAccountsSuggestField, FAccountsSuggestField,
} from '@/components'; } from '@/components';
import { inputIntent, momentFormatter, toSafeNumber } from '@/utils'; import { momentFormatter, toSafeNumber } from '@/utils';
import { Features, CLASSES } from '@/constants'; import { Features, CLASSES } from '@/constants';
import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider'; import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider';
@@ -52,6 +51,7 @@ export default function InventoryAdjustmentFormDialogFields() {
// Inventory adjustment dialog context. // Inventory adjustment dialog context.
const { accounts, branches, warehouses } = useInventoryAdjContext(); const { accounts, branches, warehouses } = useInventoryAdjContext();
const { values, setFieldValue } = useFormikContext();
// Sets the primary warehouse to form. // Sets the primary warehouse to form.
useSetPrimaryWarehouseToForm(); useSetPrimaryWarehouseToForm();
@@ -59,6 +59,17 @@ export default function InventoryAdjustmentFormDialogFields() {
// Sets the primary branch to form. // Sets the primary branch to form.
useSetPrimaryBranchToForm(); useSetPrimaryBranchToForm();
// Handle adjustment type change.
const handleAdjustmentTypeChange = (type) => {
const result = diffQuantity(
toSafeNumber(values.quantity),
toSafeNumber(values.quantity_on_hand),
type.value,
);
setFieldValue('type', type.value);
setFieldValue('new_quantity', result);
};
return ( return (
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
<Row> <Row>
@@ -66,12 +77,11 @@ export default function InventoryAdjustmentFormDialogFields() {
<Col xs={5}> <Col xs={5}>
<FormGroup <FormGroup
label={<T id={'branch'} />} label={<T id={'branch'} />}
className={classNames('form-group--select-list', Classes.FILL)} fill
> >
<BranchSelect <BranchSelect
name={'branch_id'} name={'branch_id'}
branches={branches} branches={branches}
input={BranchSelectButton}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
/> />
</FormGroup> </FormGroup>
@@ -81,7 +91,7 @@ export default function InventoryAdjustmentFormDialogFields() {
<Col xs={5}> <Col xs={5}>
<FormGroup <FormGroup
label={<T id={'warehouse'} />} label={<T id={'warehouse'} />}
className={classNames('form-group--select-list', Classes.FILL)} fill
> >
<WarehouseSelect <WarehouseSelect
name={'warehouse_id'} name={'warehouse_id'}
@@ -122,39 +132,24 @@ export default function InventoryAdjustmentFormDialogFields() {
<Col xs={5}> <Col xs={5}>
{/*------------ Adjustment type -----------*/} {/*------------ Adjustment type -----------*/}
<Field name={'type'}> <FFormGroup
{({ name={'type'}
form: { values, setFieldValue }, label={<T id={'adjustment_type'} />}
field: { value }, labelInfo={<FieldRequiredHint />}
meta: { error, touched }, fill
}) => ( fastField
<FFormGroup >
name={'type'} <FSelect
label={<T id={'adjustment_type'} />} name={'type'}
labelInfo={<FieldRequiredHint />} items={adjustmentTypes}
fill onItemChange={handleAdjustmentTypeChange}
> filterable={false}
<ListSelect valueAccessor={'value'}
items={adjustmentTypes} textAccessor={'name'}
onItemSelect={(type) => { popoverProps={{ minimal: true }}
const result = diffQuantity( fastField
toSafeNumber(values.quantity), />
toSafeNumber(values.quantity_on_hand), </FFormGroup>
type.value,
);
setFieldValue('type', type.value);
setFieldValue('new_quantity', result);
}}
filterable={false}
selectedItem={value}
selectedItemProp={'value'}
textProp={'name'}
popoverProps={{ minimal: true }}
intent={inputIntent({ error, touched })}
/>
</FFormGroup>
)}
</Field>
</Col> </Col>
</Row> </Row>

View File

@@ -73,7 +73,6 @@ function RefundCreditNoteFormFields({
<BranchSelect <BranchSelect
name={'branch_id'} name={'branch_id'}
branches={branches} branches={branches}
input={BranchSelectButton}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
/> />
</FormGroup> </FormGroup>

View File

@@ -1,19 +1,16 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { import {
FormGroup,
InputGroup,
Intent, Intent,
Classes, Classes,
Button, Button,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FastField, Form, useFormikContext, ErrorMessage } from 'formik'; import { Form, useFormikContext } from 'formik';
import classNames from 'classnames'; import classNames from 'classnames';
import { FFormGroup, FInputGroup, FormattedMessage as T } from '@/components'; import { FFormGroup, FInputGroup, FSelect, FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { inputIntent } from '@/utils'; import { FieldRequiredHint } from '@/components';
import { ListSelect, FieldRequiredHint } from '@/components';
import { useUserFormContext } from './UserFormProvider'; import { useUserFormContext } from './UserFormProvider';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import { compose } from '@/utils'; import { compose } from '@/utils';
@@ -68,31 +65,21 @@ function UserFormContent({
</FFormGroup> </FFormGroup>
{/* ----------- Role name ----------- */} {/* ----------- Role name ----------- */}
<FastField name={'role_id'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'role_id'}
<FormGroup label={<T id={'roles.label.role_name'} />}
label={<T id={'roles.label.role_name'} />} labelInfo={<FieldRequiredHint />}
labelInfo={<FieldRequiredHint />} className={classNames(CLASSES.FILL, 'form-group--role_name')}
helperText={<ErrorMessage name="role_id" />} >
className={classNames(CLASSES.FILL, 'form-group--role_name')} <FSelect
intent={inputIntent({ error, touched })} name={'role_id'}
> items={roles}
<ListSelect valueAccessor={'id'}
items={roles} textAccessor={'name'}
onItemSelect={({ id }) => { popoverProps={{ minimal: true }}
form.setFieldValue('role_id', id); disabled={isAuth}
}} />
selectedItem={value} </FFormGroup>
selectedItemProp={'id'}
textProp={'name'}
// labelProp={'id '}
popoverProps={{ minimal: true }}
intent={inputIntent({ error, touched })}
disabled={isAuth}
/>
</FormGroup>
)}
</FastField>
</div> </div>
<div className={CLASSES.DIALOG_FOOTER}> <div className={CLASSES.DIALOG_FOOTER}>

View File

@@ -105,11 +105,6 @@ function APAgingSummaryActionsBar({
/> />
</Popover> </Popover>
<Button
className={Classes.MINIMAL}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -106,11 +106,6 @@ function ARAgingSummaryActionsBar({
/> />
</Popover> </Popover>
<Button
className={Classes.MINIMAL}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -104,18 +104,6 @@ function BalanceSheetActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}

View File

@@ -107,18 +107,6 @@ function CashFlowStatementActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -104,18 +104,6 @@ function CustomersBalanceSummaryActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -106,18 +106,6 @@ function CustomersTransactionsActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -77,19 +77,6 @@ function GeneralLedgerActionsBar({
/> />
<NavbarDivider /> <NavbarDivider />
<Popover
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider />
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />} icon={<Icon icon="print-16" iconSize={16} />}

View File

@@ -102,18 +102,6 @@ function InventoryItemDetailsActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -103,18 +103,6 @@ function InventoryValuationActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />} icon={<Icon icon="print-16" iconSize={16} />}

View File

@@ -55,17 +55,29 @@ const InventoryValuationSheet = styled(FinancialSheet)`
`; `;
const InventoryValuationDataTable = styled(ReportDataTable)` const InventoryValuationDataTable = styled(ReportDataTable)`
--color-table-text-color: #252a31;
--color-table-total-text-color: #000;
--color-table-total-border: #bbb;
.bp4-dark & {
--color-table-text-color: var(--color-light-gray1);
--color-table-total-text-color: var(--color-light-gray4);
--color-table-total-border: var(--color-dark-gray5);
}
.table { .table {
.tbody { .tbody {
.tr .td { .tr .td {
border-bottom: 0; border-bottom: 0;
padding-top: 0.4rem; padding-top: 0.4rem;
padding-bottom: 0.4rem; padding-bottom: 0.4rem;
color: var(--color-table-text-color);
} }
.tr.row_type--TOTAL .td { .tr.row_type--TOTAL .td {
border-top: 1px solid #bbb; border-top: 1px solid var(--color-table-total-border);
border-bottom: 3px double var(--color-table-total-border);
font-weight: 500; font-weight: 500;
border-bottom: 3px double #000; color: var(--color-table-total-text-color);
} }
} }
} }

View File

@@ -78,19 +78,6 @@ function JournalActionsBar({
/> />
<NavbarDivider /> <NavbarDivider />
<Popover
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider />
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />} icon={<Icon icon="print-16" iconSize={16} />}

View File

@@ -103,18 +103,6 @@ function ProfitLossActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />} icon={<Icon icon="print-16" iconSize={16} />}

View File

@@ -93,17 +93,6 @@ function ProjectProfitabilitySummaryActionsBar({
icon={<Icon icon="numbers" width={23} height={16} />} icon={<Icon icon="numbers" width={23} height={16} />}
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />

View File

@@ -100,17 +100,6 @@ function PurchasesByItemsActionsBar({
icon={<Icon icon="numbers" width={23} height={16} />} icon={<Icon icon="numbers" width={23} height={16} />}
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}

View File

@@ -91,11 +91,6 @@ function RealizedGainOrLossActionsBar({
/> />
</Popover> </Popover>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -102,18 +102,6 @@ function SalesByItemsActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />} icon={<Icon icon="print-16" iconSize={16} />}

View File

@@ -104,18 +104,6 @@ function SalesTaxLiabilitySummaryActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -103,18 +103,6 @@ function TrialBalanceActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />} icon={<Icon icon="print-16" iconSize={16} />}

View File

@@ -52,6 +52,7 @@ function TrialBalanceSheetHeader({
fromDate: moment().toDate(), fromDate: moment().toDate(),
toDate: moment().toDate(), toDate: moment().toDate(),
branchesIds: [], branchesIds: [],
filterByOption: 'with-transactions',
}; };
// Initial values. // Initial values.

View File

@@ -92,11 +92,6 @@ function UnrealizedGainOrLossActionsBar({
/> />
</Popover> </Popover>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -105,11 +105,6 @@ function VendorsBalanceSummaryActionsBar({
/> />
</Popover> </Popover>
<Button
className={Classes.MINIMAL}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -106,18 +106,6 @@ function VendorsTransactionsActionsBar({
/> />
</Popover> </Popover>
<Popover
// content={}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'filter'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />
<Button <Button

View File

@@ -6,7 +6,8 @@ import classNames from 'classnames';
import styled from 'styled-components'; import styled from 'styled-components';
import { import {
ListSelect, FFormGroup,
FSelect,
FieldRequiredHint, FieldRequiredHint,
FormattedMessage as T, FormattedMessage as T,
} from '@/components'; } from '@/components';
@@ -16,29 +17,23 @@ import { inputIntent } from '@/utils';
export default function NotifyViaSMSFormFields({ notificationTypes }) { export default function NotifyViaSMSFormFields({ notificationTypes }) {
return ( return (
<NotifyViaSMSFormFieldsRoot> <NotifyViaSMSFormFieldsRoot>
<FastField name={'notification_key'}> <FFormGroup
{({ form, field: { value }, meta: { error, touched } }) => ( name={'notification_key'}
<FormGroup label={<T id={'notify_via_sms.dialog.notification_type'} />}
label={<T id={'notify_via_sms.dialog.notification_type'} />} className={classNames(CLASSES.FILL)}
className={classNames(CLASSES.FILL)} fastField
intent={inputIntent({ error, touched })} >
helperText={<ErrorMessage name={'customer_name'} />} <FSelect
> name={'notification_key'}
<ListSelect items={notificationTypes}
items={notificationTypes} valueAccessor={'key'}
selectedItemProp={'key'} textAccessor={'label'}
selectedItem={value} popoverProps={{ minimal: true }}
textProp={'label'} filterable={false}
popoverProps={{ minimal: true }} disabled={notificationTypes.length < 2}
filterable={false} fastField
onItemSelect={(notification) => { />
form.setFieldValue('notification_key', notification.key); </FFormGroup>
}}
disabled={notificationTypes.length < 2}
/>
</FormGroup>
)}
</FastField>
{/* ----------- Send Notification to ----------- */} {/* ----------- Send Notification to ----------- */}
<FastField name={'customer_name'}> <FastField name={'customer_name'}>

View File

@@ -0,0 +1,98 @@
import { ComponentType } from 'react';
import { Dispatch } from 'redux';
import { connect, MapStateToProps } from 'react-redux';
/**
* Creates a simple dispatch HOC that injects action props into a component.
* This is the DRY utility for Pattern 1: Action HOCs.
*
* @example
* ```tsx
* export interface WithAlertActionsProps {
* openAlert: (name: string) => void;
* }
*
* export const mapDispatchToProps = (dispatch: Dispatch): WithAlertActionsProps => ({
* openAlert: (name) => dispatch({ type: 'OPEN_ALERT', name }),
* });
*
* export const withAlertActions = createActionHOC<WithAlertActionsProps>(mapDispatchToProps);
* ```
*/
export function createActionHOC<TInjectedProps extends object>(
mapDispatchToProps: (dispatch: Dispatch) => TInjectedProps,
) {
return function withHOC<P extends TInjectedProps>(
WrappedComponent: ComponentType<P>,
): ComponentType<Omit<P, keyof TInjectedProps>> {
const Connected = connect(null, mapDispatchToProps)(
WrappedComponent as ComponentType<any>,
);
return Connected as unknown as ComponentType<Omit<P, keyof TInjectedProps>>;
};
}
/**
* Creates a state factory HOC that accepts an optional mapState function.
* This is the DRY utility for Pattern 2: State Factory HOCs.
*
* @example
* ```tsx
* export interface WithDrawersProps {
* isOpen: boolean;
* payload: Record<string, unknown>;
* }
*
* export const withDrawers = createStateFactoryHOC<WithDrawersProps>((state, props) => ({
* isOpen: isDrawerOpen(state, props),
* payload: getDrawerPayload(state, props),
* }));
* ```
*/
export function createStateFactoryHOC<TInjectedProps extends object>(
createMapState: () => (state: any, props: any) => TInjectedProps,
) {
type MapStateFn<T> = (mapped: TInjectedProps, state?: unknown, props?: unknown) => T;
return function withHOC<P, T = TInjectedProps>(mapState?: MapStateFn<T>) {
const mapStateToProps = createMapState();
const wrappedMapState = (state: any, props: any) => {
const mapped = mapStateToProps(state, props);
return mapState ? mapState(mapped, state, props) : mapped;
};
return function <C extends ComponentType<P>>(WrappedComponent: C) {
return connect(wrappedMapState)(WrappedComponent as ComponentType<any>) as unknown as ComponentType<
Omit<P, keyof (T extends TInjectedProps ? T : TInjectedProps)>
>;
};
};
}
/**
* Creates a simple state HOC that directly connects state to component props.
* This is for HOCs that don't use the factory pattern.
*
* @example
* ```tsx
* export interface WithCurrencyDetailProps {
* currency: Record<string, unknown> | null;
* }
*
* export const withCurrencyDetail = createStateHOC<WithCurrencyDetailProps>((state, props) => ({
* currency: getCurrencyByCode(state, props),
* }));
* ```
*/
export function createStateHOC<TInjectedProps extends object>(
mapStateToProps: (state: any, props: any) => TInjectedProps,
) {
return function withHOC<P extends TInjectedProps>(
WrappedComponent: ComponentType<P>,
): ComponentType<Omit<P, keyof TInjectedProps>> {
const Connected = connect(mapStateToProps)(
WrappedComponent as ComponentType<any>,
);
return Connected as unknown as ComponentType<Omit<P, keyof TInjectedProps>>;
};
}