feat: profit report
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 6s
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 6s
This commit is contained in:
parent
54dba11dc4
commit
b4df1a5d4e
1 changed files with 157 additions and 83 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
import config from "../config.json";
|
||||||
import {
|
import {
|
||||||
Customer,
|
Customer,
|
||||||
CustomerBranch,
|
CustomerBranch,
|
||||||
|
|
@ -11,9 +12,12 @@ import prisma from "../db";
|
||||||
import { createPermCondition } from "../services/permission";
|
import { createPermCondition } from "../services/permission";
|
||||||
import { RequestWithUser } from "../interfaces/user";
|
import { RequestWithUser } from "../interfaces/user";
|
||||||
import { PaymentStatus } from "../generated/kysely/types";
|
import { PaymentStatus } from "../generated/kysely/types";
|
||||||
|
import { precisionRound } from "../utils/arithmetic";
|
||||||
|
|
||||||
const permissionCondCompany = createPermCondition((_) => true);
|
const permissionCondCompany = createPermCondition((_) => true);
|
||||||
|
|
||||||
|
const VAT_DEFAULT = config.vat;
|
||||||
|
|
||||||
@Route("/api/v1/report")
|
@Route("/api/v1/report")
|
||||||
@Security("keycloak")
|
@Security("keycloak")
|
||||||
@Tags("Report")
|
@Tags("Report")
|
||||||
|
|
@ -127,7 +131,8 @@ export class StatsController extends Controller {
|
||||||
@Query() startDate?: Date,
|
@Query() startDate?: Date,
|
||||||
@Query() endDate?: Date,
|
@Query() endDate?: Date,
|
||||||
) {
|
) {
|
||||||
const record = await prisma.product.findMany({
|
await prisma.$transaction(async (tx) => {
|
||||||
|
const record = await tx.product.findMany({
|
||||||
include: {
|
include: {
|
||||||
quotationProductServiceList: {
|
quotationProductServiceList: {
|
||||||
include: {
|
include: {
|
||||||
|
|
@ -173,7 +178,7 @@ export class StatsController extends Controller {
|
||||||
take: limit,
|
take: limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
const doing = await prisma.quotationProductServiceList.groupBy({
|
const doing = await tx.quotationProductServiceList.groupBy({
|
||||||
_count: true,
|
_count: true,
|
||||||
by: "productId",
|
by: "productId",
|
||||||
where: {
|
where: {
|
||||||
|
|
@ -202,7 +207,7 @@ export class StatsController extends Controller {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const order = await prisma.quotationProductServiceList.groupBy({
|
const order = await tx.quotationProductServiceList.groupBy({
|
||||||
_count: true,
|
_count: true,
|
||||||
by: "productId",
|
by: "productId",
|
||||||
where: {
|
where: {
|
||||||
|
|
@ -224,6 +229,7 @@ export class StatsController extends Controller {
|
||||||
createdAt: v.createdAt,
|
createdAt: v.createdAt,
|
||||||
updatedAt: v.updatedAt,
|
updatedAt: v.updatedAt,
|
||||||
}));
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("sale")
|
@Get("sale")
|
||||||
|
|
@ -306,4 +312,72 @@ export class StatsController extends Controller {
|
||||||
{ byProductGroup: [], bySale: [], byCustomer: [] },
|
{ byProductGroup: [], bySale: [], byCustomer: [] },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("profit")
|
||||||
|
async profit(
|
||||||
|
@Request() req: RequestWithUser,
|
||||||
|
@Query() limit?: number,
|
||||||
|
@Query() startDate?: Date,
|
||||||
|
@Query() endDate?: Date,
|
||||||
|
) {
|
||||||
|
const record = await prisma.quotationProductServiceList.findMany({
|
||||||
|
include: {
|
||||||
|
product: {
|
||||||
|
select: {
|
||||||
|
agentPrice: true,
|
||||||
|
agentPriceCalcVat: true,
|
||||||
|
agentPriceVatIncluded: true,
|
||||||
|
serviceCharge: true,
|
||||||
|
serviceChargeCalcVat: true,
|
||||||
|
serviceChargeVatIncluded: true,
|
||||||
|
price: true,
|
||||||
|
calcVat: true,
|
||||||
|
vatIncluded: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
quotation: {
|
||||||
|
select: {
|
||||||
|
agentPrice: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
quotation: {
|
||||||
|
quotationStatus: {
|
||||||
|
in: [
|
||||||
|
QuotationStatus.PaymentInProcess,
|
||||||
|
QuotationStatus.PaymentSuccess,
|
||||||
|
QuotationStatus.ProcessComplete,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
registeredBranch: {
|
||||||
|
OR: permissionCondCompany(req.user),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
take: limit,
|
||||||
|
});
|
||||||
|
|
||||||
|
let income = 0;
|
||||||
|
let expenses = 0;
|
||||||
|
let netProfit = 0;
|
||||||
|
|
||||||
|
record.forEach((v) => {
|
||||||
|
const originalPrice = v.product.serviceCharge;
|
||||||
|
const productExpenses = precisionRound(
|
||||||
|
originalPrice + (v.product.serviceChargeVatIncluded ? 0 : originalPrice * VAT_DEFAULT),
|
||||||
|
);
|
||||||
|
const finalPrice = v.pricePerUnit * v.amount * (1 + config.vat);
|
||||||
|
|
||||||
|
income += finalPrice;
|
||||||
|
expenses += productExpenses;
|
||||||
|
netProfit += finalPrice - productExpenses;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
income: precisionRound(income),
|
||||||
|
expenses: precisionRound(expenses),
|
||||||
|
netProfit: precisionRound(netProfit),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue