fix: bugs sprint

This commit is contained in:
Ahmed Bouhuolia
2025-12-18 13:48:12 +02:00
parent 17651e0768
commit 636d206b0e
9 changed files with 86 additions and 71 deletions

View File

@@ -5,10 +5,10 @@ import { ExpensesSampleData } from './constants';
import { CreateExpense } from './commands/CreateExpense.service'; import { CreateExpense } from './commands/CreateExpense.service';
import { CreateExpenseDto } from './dtos/Expense.dto'; import { CreateExpenseDto } from './dtos/Expense.dto';
import { ImportableService } from '../Import/decorators/Import.decorator'; import { ImportableService } from '../Import/decorators/Import.decorator';
import { ManualJournal } from '../ManualJournals/models/ManualJournal'; import { Expense } from './models/Expense.model';
@Injectable() @Injectable()
@ImportableService({ name: ManualJournal.name }) @ImportableService({ name: Expense.name })
export class ExpensesImportable extends Importable { export class ExpensesImportable extends Importable {
constructor(private readonly createExpenseService: CreateExpense) { constructor(private readonly createExpenseService: CreateExpense) {
super(); super();

View File

@@ -18,7 +18,7 @@ import { CurrencyParsingDTOs } from './_constants';
export class ImportFileDataTransformer { export class ImportFileDataTransformer {
constructor( constructor(
private readonly resource: ResourceService, private readonly resource: ResourceService,
) {} ) { }
/** /**
* Parses the given sheet data before passing to the service layer. * Parses the given sheet data before passing to the service layer.

View File

@@ -6,7 +6,7 @@ import { ContextIdFactory, ModuleRef } from '@nestjs/core';
@Injectable() @Injectable()
export class ImportableRegistry { export class ImportableRegistry {
constructor(private readonly moduleRef: ModuleRef) {} constructor(private readonly moduleRef: ModuleRef) { }
/** /**
* Retrieves the importable service instance of the given resource name. * Retrieves the importable service instance of the given resource name.
* @param {string} name * @param {string} name
@@ -15,6 +15,12 @@ export class ImportableRegistry {
public async getImportable(name: string) { public async getImportable(name: string) {
const _name = this.sanitizeResourceName(name); const _name = this.sanitizeResourceName(name);
const importable = getImportableService(_name); const importable = getImportableService(_name);
if (!importable) {
throw new Error(
`No importable service found for resource "${_name}". Make sure the resource has an @ImportableService decorator registered.`,
);
}
const contextId = ContextIdFactory.create(); const contextId = ContextIdFactory.create();
const importableInstance = await this.moduleRef.resolve(importable, contextId, { const importableInstance = await this.moduleRef.resolve(importable, contextId, {

View File

@@ -253,28 +253,28 @@ export const getResourceColumns = (resourceColumns: {
}) => { }) => {
const mapColumn = const mapColumn =
(group: string) => (group: string) =>
([fieldKey, { name, importHint, required, order, ...field }]: [ ([fieldKey, { name, importHint, required, order, ...field }]: [
string, string,
IModelMetaField2, IModelMetaField2,
]) => { ]) => {
const extra: Record<string, any> = {}; const extra: Record<string, any> = {};
const key = fieldKey; const key = fieldKey;
if (group) { if (group) {
extra.group = group; extra.group = group;
} }
if (field.fieldType === 'collection') { if (field.fieldType === 'collection') {
extra.fields = mapColumns(field.fields, key); extra.fields = mapColumns(field.fields, key);
} }
return { return {
key, key,
name, name,
required, required,
hint: importHint, hint: importHint,
order, order,
...extra, ...extra,
};
}; };
};
const sortColumn = (a, b) => const sortColumn = (a, b) =>
a.order && b.order ? a.order - b.order : a.order ? -1 : b.order ? 1 : 0; a.order && b.order ? a.order - b.order : a.order ? -1 : b.order ? 1 : 0;
@@ -287,49 +287,49 @@ export const getResourceColumns = (resourceColumns: {
// Prases the given object value based on the field key type. // Prases the given object value based on the field key type.
export const valueParser = export const valueParser =
(fields: ResourceMetaFieldsMap, tenantModels: any, trx?: Knex.Transaction) => (fields: ResourceMetaFieldsMap, tenantModels: any, trx?: Knex.Transaction) =>
async (value: any, key: string, group = '') => { async (value: any, key: string, group = '') => {
let _value = value; let _value = value;
const fieldKey = key.includes('.') ? key.split('.')[0] : key; const fieldKey = key.includes('.') ? key.split('.')[0] : key;
const field = group ? fields[group]?.fields[fieldKey] : fields[fieldKey]; const field = group ? fields[group]?.fields[fieldKey] : fields[fieldKey];
// Parses the boolean value. // Parses the boolean value.
if (field.fieldType === 'boolean') { if (field.fieldType === 'boolean') {
_value = parseBoolean(value); _value = parseBoolean(value);
// Parses the enumeration value. // Parses the enumeration value.
} else if (field.fieldType === 'enumeration') { } else if (field.fieldType === 'enumeration') {
const option = get(field, 'options', []).find( const option = get(field, 'options', []).find(
(option) => option.label?.toLowerCase() === value?.toLowerCase(), (option) => option.label?.toLowerCase() === value?.toLowerCase(),
); );
_value = get(option, 'key'); _value = get(option, 'key');
// Parses the numeric value. // Parses the numeric value.
} else if (field.fieldType === 'number') { } else if (field.fieldType === 'number') {
_value = multiNumberParse(value); _value = multiNumberParse(value);
// Parses the relation value. // Parses the relation value.
} else if (field.fieldType === 'relation') { } else if (field.fieldType === 'relation') {
const RelationModel = tenantModels[field.relationModel]; const RelationModel = tenantModels[field.relationModel];
if (!RelationModel) { if (!RelationModel) {
throw new Error(`The relation model of ${key} field is not exist.`); throw new Error(`The relation model of ${key} field is not exist.`);
} }
const relationQuery = RelationModel.query(trx); const relationQuery = RelationModel.query(trx);
const relationKeys = castArray(field?.relationImportMatch); const relationKeys = castArray(field?.relationImportMatch);
relationQuery.where(function () { relationQuery.where(function () {
relationKeys.forEach((relationKey: string) => { relationKeys.forEach((relationKey: string) => {
this.orWhereRaw('LOWER(??) = LOWER(?)', [relationKey, value]); this.orWhereRaw('LOWER(??) = LOWER(?)', [relationKey, value]);
});
}); });
}); const result = await relationQuery.first();
const result = await relationQuery.first(); _value = get(result, 'id');
_value = get(result, 'id'); } else if (field.fieldType === 'collection') {
} else if (field.fieldType === 'collection') { const ObjectFieldKey = key.includes('.') ? key.split('.')[1] : key;
const ObjectFieldKey = key.includes('.') ? key.split('.')[1] : key; const _valueParser = valueParser(fields, tenantModels);
const _valueParser = valueParser(fields, tenantModels); _value = await _valueParser(value, ObjectFieldKey, fieldKey);
_value = await _valueParser(value, ObjectFieldKey, fieldKey); }
} return _value;
return _value; };
};
/** /**
* Parses the field key and detarmines the key path. * Parses the field key and detarmines the key path.

View File

@@ -5,10 +5,10 @@ import { Importable } from '@/modules/Import/Importable';
import { CreateSaleInvoiceDto } from '../dtos/SaleInvoice.dto'; import { CreateSaleInvoiceDto } from '../dtos/SaleInvoice.dto';
import { SaleInvoicesSampleData } from '../constants'; import { SaleInvoicesSampleData } from '../constants';
import { ImportableService } from '@/modules/Import/decorators/Import.decorator'; import { ImportableService } from '@/modules/Import/decorators/Import.decorator';
import { ManualJournal } from '@/modules/ManualJournals/models/ManualJournal'; import { SaleInvoice } from '../models/SaleInvoice';
@Injectable() @Injectable()
@ImportableService({ name: ManualJournal.name }) @ImportableService({ name: SaleInvoice.name })
export class SaleInvoicesImportable extends Importable { export class SaleInvoicesImportable extends Importable {
constructor(private readonly createInvoiceService: CreateSaleInvoice) { constructor(private readonly createInvoiceService: CreateSaleInvoice) {
super(); super();

View File

@@ -107,4 +107,4 @@ const modelProviders = models.map((model) => RegisterTenancyModel(model));
imports: [...modelProviders], imports: [...modelProviders],
exports: [...modelProviders], exports: [...modelProviders],
}) })
export class TenancyModelsModule {} export class TenancyModelsModule { }

View File

@@ -102,6 +102,7 @@ function AccountsSuggestFieldRoot({
return ( return (
<FSuggest <FSuggest
items={filteredAccounts} items={filteredAccounts}
itemPredicate={filterAccountsPredicater}
onCreateItemSelect={handleCreateItemSelect} onCreateItemSelect={handleCreateItemSelect}
valueAccessor="id" valueAccessor="id"
textAccessor="name" textAccessor="name"

View File

@@ -48,7 +48,7 @@ function ExpenseForm({
createExpenseMutate, createExpenseMutate,
expense, expense,
expenseId, expenseId,
submitPayload, submitPayloadRef,
} = useExpenseFormContext(); } = useExpenseFormContext();
const isNewMode = !expenseId; const isNewMode = !expenseId;
@@ -86,9 +86,12 @@ function ExpenseForm({
return; return;
} }
// Get submit payload from ref for synchronous access
const currentSubmitPayload = submitPayloadRef?.current || {};
const form = { const form = {
...transformFormValuesToRequest(values), ...transformFormValuesToRequest(values),
publish: submitPayload.publish, publish: currentSubmitPayload.publish,
}; };
// Handle request success. // Handle request success.
const handleSuccess = (response) => { const handleSuccess = (response) => {
@@ -103,10 +106,10 @@ function ExpenseForm({
}); });
setSubmitting(false); setSubmitting(false);
if (submitPayload.redirect) { if (currentSubmitPayload.redirect) {
history.push('/expenses'); history.push('/expenses');
} }
if (submitPayload.resetForm) { if (currentSubmitPayload.resetForm) {
resetForm(); resetForm();
} }
}; };

View File

@@ -59,8 +59,13 @@ function ExpenseFormPageProvider({ query, expenseId, ...props }) {
const { mutateAsync: createExpenseMutate } = useCreateExpense(); const { mutateAsync: createExpenseMutate } = useCreateExpense();
const { mutateAsync: editExpenseMutate } = useEditExpense(); const { mutateAsync: editExpenseMutate } = useEditExpense();
// Submit form payload. // Submit form payload - using ref for synchronous access.
const [submitPayload, setSubmitPayload] = React.useState({}); const submitPayloadRef = React.useRef({});
// Setter to update the ref.
const setSubmitPayload = React.useCallback((payload) => {
submitPayloadRef.current = payload;
}, []);
// Detarmines whether the form in new mode. // Detarmines whether the form in new mode.
const isNewMode = !expenseId; const isNewMode = !expenseId;
@@ -69,7 +74,7 @@ function ExpenseFormPageProvider({ query, expenseId, ...props }) {
const provider = { const provider = {
isNewMode, isNewMode,
expenseId, expenseId,
submitPayload, submitPayloadRef, // Expose ref for synchronous access
currencies, currencies,
customers, customers,