fix(expenses): ajusted mileage logic
This commit is contained in:
parent
3b4dd9ddb5
commit
f8f4ad5a83
|
|
@ -1,21 +1,19 @@
|
|||
import { Transform, Type } from "class-transformer";
|
||||
import { IsDefined, IsNumber, IsOptional, IsString, maxLength, MaxLength, Min, ValidateIf, ValidateNested } from "class-validator";
|
||||
import { IsNumber, IsOptional, IsString, MaxLength, Min, ValidateIf, ValidateNested } from "class-validator";
|
||||
|
||||
export class ExpensePayloadDto {
|
||||
@IsString()
|
||||
type!: string;
|
||||
|
||||
@ValidateIf(o => (o.type ?? '').toUpperCase() !== 'MILEAGE')
|
||||
@IsDefined()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
amount!: number;
|
||||
amount?: number;
|
||||
|
||||
@ValidateIf(o => (o.type ?? '').toUpperCase() === 'MILEAGE')
|
||||
@IsDefined()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
mileage!: number;
|
||||
mileage?: number;
|
||||
|
||||
@IsString()
|
||||
@MaxLength(280)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,15 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
orderBy: [{ date: 'asc' }, { id: 'asc' }],
|
||||
});
|
||||
|
||||
return rows.map(this.mapDbToDayResponse);
|
||||
return rows.map((r) =>
|
||||
this.mapDbToDayResponse({
|
||||
date: r.date,
|
||||
amount: r.amount ?? 0,
|
||||
mileage: r.mileage ?? 0,
|
||||
comment: r.comment,
|
||||
is_approved: r.is_approved,
|
||||
bank_code: r.bank_code,
|
||||
}));
|
||||
};
|
||||
|
||||
const normalizePayload = async (payload: {
|
||||
|
|
@ -85,19 +93,39 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
type: string;
|
||||
bank_code_id: number;
|
||||
amount: Prisma.Decimal;
|
||||
mileage: number | null;
|
||||
comment: string;
|
||||
attachment: string | null;
|
||||
}> => {
|
||||
const type = this.normalizeType(payload.type);
|
||||
const comment = this.assertAndTrimComment(payload.comment);
|
||||
const attachment = payload.attachment?.trim()?.length ? payload.attachment.trim() : null;
|
||||
|
||||
const { id: bank_code_id, modifier } = await this.lookupBankCodeOrThrow(tx, type);
|
||||
const amount = this.computeAmountDecimal(type, payload, modifier);
|
||||
let amount = this.computeAmountDecimal(type, payload, modifier);
|
||||
let mileage: number | null = null;
|
||||
|
||||
if (type === 'MILEAGE') {
|
||||
mileage = Number(payload.mileage ?? 0);
|
||||
if (!(mileage > 0)) {
|
||||
throw new BadRequestException('Mileage required and must be > 0 for type MILEAGE');
|
||||
}
|
||||
|
||||
const amountNumber = computeMileageAmount(mileage, modifier);
|
||||
amount = new Prisma.Decimal(amountNumber);
|
||||
|
||||
} else {
|
||||
if (!(typeof payload.amount === 'number' && payload.amount >= 0)) {
|
||||
throw new BadRequestException('Amount required for non-MILEAGE expense');
|
||||
}
|
||||
amount = new Prisma.Decimal(payload.amount);
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
bank_code_id,
|
||||
amount,
|
||||
mileage,
|
||||
comment,
|
||||
attachment
|
||||
};
|
||||
|
|
@ -106,6 +134,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
const findExactOld = async (norm: {
|
||||
bank_code_id: number;
|
||||
amount: Prisma.Decimal;
|
||||
mileage: number | null;
|
||||
comment: string;
|
||||
attachment: string | null;
|
||||
}) => {
|
||||
|
|
@ -117,6 +146,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
amount: norm.amount,
|
||||
comment: norm.comment,
|
||||
attachment: norm.attachment,
|
||||
...(norm.mileage !== null ? { mileage: norm.mileage } : { mileage: null }),
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
|
@ -145,7 +175,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
date: dateOnly,
|
||||
bank_code_id: new_exp.bank_code_id,
|
||||
amount: new_exp.amount,
|
||||
mileage: null,
|
||||
mileage: new_exp.mileage,
|
||||
comment: new_exp.comment,
|
||||
attachment: new_exp.attachment,
|
||||
is_approved: false,
|
||||
|
|
@ -153,7 +183,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
});
|
||||
action = 'created';
|
||||
}
|
||||
|
||||
//-------------------- UPDATE --------------------
|
||||
else if(old_expense && new_expense) {
|
||||
const oldNorm = await normalizePayload(old_expense);
|
||||
const existing = await findExactOld(oldNorm);
|
||||
|
|
@ -170,7 +200,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
data: {
|
||||
bank_code_id: new_exp.bank_code_id,
|
||||
amount: new_exp.amount,
|
||||
mileage: null,
|
||||
mileage: new_exp.mileage,
|
||||
comment: new_exp.comment,
|
||||
attachment: new_exp.attachment,
|
||||
},
|
||||
|
|
@ -188,7 +218,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
}
|
||||
|
||||
|
||||
//helpers imported from utils and repos.
|
||||
//-------------------- helpers --------------------
|
||||
private readonly normalizeType = (type: string): string =>
|
||||
normalizeTypeUtil(type);
|
||||
|
||||
|
|
@ -210,7 +240,10 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
|
||||
private readonly computeAmountDecimal = (
|
||||
type: string,
|
||||
payload: { amount?: number; mileage?: number;},
|
||||
payload: {
|
||||
amount?: number;
|
||||
mileage?: number;
|
||||
},
|
||||
modifier: number,
|
||||
): Prisma.Decimal => {
|
||||
if(type === 'MILEAGE') {
|
||||
|
|
@ -224,6 +257,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
|||
private readonly mapDbToDayResponse = (row: {
|
||||
date: Date;
|
||||
amount: Prisma.Decimal | number | string;
|
||||
mileage: Prisma.Decimal | number | string;
|
||||
comment: string;
|
||||
is_approved: boolean;
|
||||
bank_code: { type: string } | null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { BadRequestException } from "@nestjs/common";
|
||||
import { DayExpenseResponse } from "../types and interfaces/expenses.types.interfaces";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
//uppercase and trim for validation
|
||||
export function normalizeType(type: string): string {
|
||||
|
|
@ -49,17 +50,20 @@ export function toNumberSafe(value: DecimalLike): number {
|
|||
//map of a row for DayExpenseResponse
|
||||
export function mapDbExpenseToDayResponse(row: {
|
||||
date: Date;
|
||||
amount: DecimalLike;
|
||||
amount: Prisma.Decimal | number | string | null;
|
||||
mileage?: Prisma.Decimal | number | string | null;
|
||||
comment: string;
|
||||
is_approved: boolean;
|
||||
bank_code?: { type?: string | null } | null;
|
||||
}): DayExpenseResponse {
|
||||
const yyyyMmDd = row.date.toISOString().slice(0,10);
|
||||
const toNum = (value: any)=> (value == null ? 0 : Number(value));
|
||||
return {
|
||||
date: yyyyMmDd,
|
||||
type: normalizeType(row.bank_code?.type ?? 'UNKNOWN'),
|
||||
amount: toNumberSafe(row.amount),
|
||||
amount: toNum(row.amount),
|
||||
comment: row.comment,
|
||||
is_approved: row.is_approved,
|
||||
...(row.mileage !== null ? { mileage: toNum(row.mileage) }: {}),
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user