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 { 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 {
|
export class ExpensePayloadDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
type!: string;
|
type!: string;
|
||||||
|
|
||||||
@ValidateIf(o => (o.type ?? '').toUpperCase() !== 'MILEAGE')
|
@ValidateIf(o => (o.type ?? '').toUpperCase() !== 'MILEAGE')
|
||||||
@IsDefined()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
amount!: number;
|
amount?: number;
|
||||||
|
|
||||||
@ValidateIf(o => (o.type ?? '').toUpperCase() === 'MILEAGE')
|
@ValidateIf(o => (o.type ?? '').toUpperCase() === 'MILEAGE')
|
||||||
@IsDefined()
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
mileage!: number;
|
mileage?: number;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@MaxLength(280)
|
@MaxLength(280)
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,15 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
orderBy: [{ date: 'asc' }, { id: 'asc' }],
|
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: {
|
const normalizePayload = async (payload: {
|
||||||
|
|
@ -85,19 +93,39 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
type: string;
|
type: string;
|
||||||
bank_code_id: number;
|
bank_code_id: number;
|
||||||
amount: Prisma.Decimal;
|
amount: Prisma.Decimal;
|
||||||
|
mileage: number | null;
|
||||||
comment: string;
|
comment: string;
|
||||||
attachment: string | null;
|
attachment: string | null;
|
||||||
}> => {
|
}> => {
|
||||||
const type = this.normalizeType(payload.type);
|
const type = this.normalizeType(payload.type);
|
||||||
const comment = this.assertAndTrimComment(payload.comment);
|
const comment = this.assertAndTrimComment(payload.comment);
|
||||||
const attachment = payload.attachment?.trim()?.length ? payload.attachment.trim() : null;
|
const attachment = payload.attachment?.trim()?.length ? payload.attachment.trim() : null;
|
||||||
|
|
||||||
const { id: bank_code_id, modifier } = await this.lookupBankCodeOrThrow(tx, type);
|
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 {
|
return {
|
||||||
type,
|
type,
|
||||||
bank_code_id,
|
bank_code_id,
|
||||||
amount,
|
amount,
|
||||||
|
mileage,
|
||||||
comment,
|
comment,
|
||||||
attachment
|
attachment
|
||||||
};
|
};
|
||||||
|
|
@ -106,6 +134,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
const findExactOld = async (norm: {
|
const findExactOld = async (norm: {
|
||||||
bank_code_id: number;
|
bank_code_id: number;
|
||||||
amount: Prisma.Decimal;
|
amount: Prisma.Decimal;
|
||||||
|
mileage: number | null;
|
||||||
comment: string;
|
comment: string;
|
||||||
attachment: string | null;
|
attachment: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -117,6 +146,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
amount: norm.amount,
|
amount: norm.amount,
|
||||||
comment: norm.comment,
|
comment: norm.comment,
|
||||||
attachment: norm.attachment,
|
attachment: norm.attachment,
|
||||||
|
...(norm.mileage !== null ? { mileage: norm.mileage } : { mileage: null }),
|
||||||
},
|
},
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
|
|
@ -145,7 +175,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
date: dateOnly,
|
date: dateOnly,
|
||||||
bank_code_id: new_exp.bank_code_id,
|
bank_code_id: new_exp.bank_code_id,
|
||||||
amount: new_exp.amount,
|
amount: new_exp.amount,
|
||||||
mileage: null,
|
mileage: new_exp.mileage,
|
||||||
comment: new_exp.comment,
|
comment: new_exp.comment,
|
||||||
attachment: new_exp.attachment,
|
attachment: new_exp.attachment,
|
||||||
is_approved: false,
|
is_approved: false,
|
||||||
|
|
@ -153,7 +183,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
});
|
});
|
||||||
action = 'created';
|
action = 'created';
|
||||||
}
|
}
|
||||||
|
//-------------------- UPDATE --------------------
|
||||||
else if(old_expense && new_expense) {
|
else if(old_expense && new_expense) {
|
||||||
const oldNorm = await normalizePayload(old_expense);
|
const oldNorm = await normalizePayload(old_expense);
|
||||||
const existing = await findExactOld(oldNorm);
|
const existing = await findExactOld(oldNorm);
|
||||||
|
|
@ -170,7 +200,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
data: {
|
data: {
|
||||||
bank_code_id: new_exp.bank_code_id,
|
bank_code_id: new_exp.bank_code_id,
|
||||||
amount: new_exp.amount,
|
amount: new_exp.amount,
|
||||||
mileage: null,
|
mileage: new_exp.mileage,
|
||||||
comment: new_exp.comment,
|
comment: new_exp.comment,
|
||||||
attachment: new_exp.attachment,
|
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 =>
|
private readonly normalizeType = (type: string): string =>
|
||||||
normalizeTypeUtil(type);
|
normalizeTypeUtil(type);
|
||||||
|
|
||||||
|
|
@ -210,7 +240,10 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
|
|
||||||
private readonly computeAmountDecimal = (
|
private readonly computeAmountDecimal = (
|
||||||
type: string,
|
type: string,
|
||||||
payload: { amount?: number; mileage?: number;},
|
payload: {
|
||||||
|
amount?: number;
|
||||||
|
mileage?: number;
|
||||||
|
},
|
||||||
modifier: number,
|
modifier: number,
|
||||||
): Prisma.Decimal => {
|
): Prisma.Decimal => {
|
||||||
if(type === 'MILEAGE') {
|
if(type === 'MILEAGE') {
|
||||||
|
|
@ -224,6 +257,7 @@ export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||||
private readonly mapDbToDayResponse = (row: {
|
private readonly mapDbToDayResponse = (row: {
|
||||||
date: Date;
|
date: Date;
|
||||||
amount: Prisma.Decimal | number | string;
|
amount: Prisma.Decimal | number | string;
|
||||||
|
mileage: Prisma.Decimal | number | string;
|
||||||
comment: string;
|
comment: string;
|
||||||
is_approved: boolean;
|
is_approved: boolean;
|
||||||
bank_code: { type: string } | null;
|
bank_code: { type: string } | null;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { BadRequestException } from "@nestjs/common";
|
import { BadRequestException } from "@nestjs/common";
|
||||||
import { DayExpenseResponse } from "../types and interfaces/expenses.types.interfaces";
|
import { DayExpenseResponse } from "../types and interfaces/expenses.types.interfaces";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
|
||||||
//uppercase and trim for validation
|
//uppercase and trim for validation
|
||||||
export function normalizeType(type: string): string {
|
export function normalizeType(type: string): string {
|
||||||
|
|
@ -49,17 +50,20 @@ export function toNumberSafe(value: DecimalLike): number {
|
||||||
//map of a row for DayExpenseResponse
|
//map of a row for DayExpenseResponse
|
||||||
export function mapDbExpenseToDayResponse(row: {
|
export function mapDbExpenseToDayResponse(row: {
|
||||||
date: Date;
|
date: Date;
|
||||||
amount: DecimalLike;
|
amount: Prisma.Decimal | number | string | null;
|
||||||
|
mileage?: Prisma.Decimal | number | string | null;
|
||||||
comment: string;
|
comment: string;
|
||||||
is_approved: boolean;
|
is_approved: boolean;
|
||||||
bank_code?: { type?: string | null } | null;
|
bank_code?: { type?: string | null } | null;
|
||||||
}): DayExpenseResponse {
|
}): DayExpenseResponse {
|
||||||
const yyyyMmDd = row.date.toISOString().slice(0,10);
|
const yyyyMmDd = row.date.toISOString().slice(0,10);
|
||||||
|
const toNum = (value: any)=> (value == null ? 0 : Number(value));
|
||||||
return {
|
return {
|
||||||
date: yyyyMmDd,
|
date: yyyyMmDd,
|
||||||
type: normalizeType(row.bank_code?.type ?? 'UNKNOWN'),
|
type: normalizeType(row.bank_code?.type ?? 'UNKNOWN'),
|
||||||
amount: toNumberSafe(row.amount),
|
amount: toNum(row.amount),
|
||||||
comment: row.comment,
|
comment: row.comment,
|
||||||
is_approved: row.is_approved,
|
is_approved: row.is_approved,
|
||||||
|
...(row.mileage !== null ? { mileage: toNum(row.mileage) }: {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user