mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 22:18:12 +00:00
booking cancellation
This commit is contained in:
@@ -63,6 +63,23 @@ export class BillingController {
|
||||
});
|
||||
}
|
||||
|
||||
@Get("invoices/summary")
|
||||
@ApiOperation({
|
||||
summary:
|
||||
"Total collected (paidAmount) across every filtered invoice, grouped by currency",
|
||||
})
|
||||
async collectedSummary(
|
||||
@Query() query: FilterInvoiceDto,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
const allowed =
|
||||
await this.userTradeAccessService.resolveAllowedDirections(user);
|
||||
return this.billingService.collectedSummary({
|
||||
...query,
|
||||
tradeDirections: allowed ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@Get("invoices/:id")
|
||||
@ApiOperation({ summary: "Get an invoice with its line items" })
|
||||
findById(@Param("id", ParseUUIDPipe) id: string) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "@nestjs/common";
|
||||
import { EventEmitter2 } from "@nestjs/event-emitter";
|
||||
import { logCtx } from "@edr/api-common";
|
||||
import { DataSource, EntityManager, In } from "typeorm";
|
||||
import { DataSource, EntityManager, In, SelectQueryBuilder } from "typeorm";
|
||||
|
||||
import { Booking } from "../bookings/entities/booking.entity";
|
||||
// Entity-only import (no module edge): portal reads resolve shipping-line
|
||||
@@ -192,6 +192,40 @@ export class BillingService {
|
||||
* company (customer detail "Invoices" tab) and/or status/search (global
|
||||
* invoices page).
|
||||
*/
|
||||
/** Same list filters `findAllPaginated` and `collectedSummary` both narrow by. */
|
||||
private applyInvoiceFilters(
|
||||
qb: SelectQueryBuilder<Invoice>,
|
||||
filter: {
|
||||
companyId?: string;
|
||||
status?: Freight.InvoiceStatus;
|
||||
search?: string;
|
||||
tradeDirections?: string[];
|
||||
},
|
||||
) {
|
||||
if (filter.companyId) {
|
||||
qb.andWhere("invoice.companyId = :companyId", {
|
||||
companyId: filter.companyId,
|
||||
});
|
||||
}
|
||||
if (filter.status) {
|
||||
qb.andWhere("invoice.status = :status", { status: filter.status });
|
||||
}
|
||||
if (filter.search) {
|
||||
qb.andWhere(
|
||||
"(invoice.invoiceNumber ILIKE :search OR invoice.sourceId ILIKE :search)",
|
||||
{ search: `%${filter.search}%` },
|
||||
);
|
||||
}
|
||||
if (filter.tradeDirections) {
|
||||
applyBookingRefDirectionScope(
|
||||
qb,
|
||||
"invoice.source_id",
|
||||
filter.tradeDirections,
|
||||
);
|
||||
}
|
||||
return qb;
|
||||
}
|
||||
|
||||
async findAllPaginated(
|
||||
filter: {
|
||||
companyId?: string;
|
||||
@@ -215,33 +249,43 @@ export class BillingService {
|
||||
.skip((page - 1) * pageSize)
|
||||
.take(pageSize);
|
||||
|
||||
if (filter.companyId) {
|
||||
qb.andWhere("invoice.companyId = :companyId", {
|
||||
companyId: filter.companyId,
|
||||
});
|
||||
}
|
||||
if (filter.status) {
|
||||
qb.andWhere("invoice.status = :status", { status: filter.status });
|
||||
}
|
||||
if (filter.search) {
|
||||
qb.andWhere(
|
||||
"(invoice.invoiceNumber ILIKE :search OR invoice.sourceId ILIKE :search)",
|
||||
{ search: `%${filter.search}%` },
|
||||
);
|
||||
}
|
||||
|
||||
if (filter.tradeDirections) {
|
||||
applyBookingRefDirectionScope(
|
||||
qb,
|
||||
"invoice.source_id",
|
||||
filter.tradeDirections,
|
||||
);
|
||||
}
|
||||
this.applyInvoiceFilters(qb, filter);
|
||||
|
||||
const [items, total] = await qb.getManyAndCount();
|
||||
return { items, total };
|
||||
}
|
||||
|
||||
/**
|
||||
* Total collected (`paidAmount`) across every invoice matching the same
|
||||
* filters as `findAllPaginated`, grouped by currency — unpaginated, so the
|
||||
* invoices summary card reflects the whole filtered set, not just the
|
||||
* visible page.
|
||||
*/
|
||||
async collectedSummary(
|
||||
filter: {
|
||||
companyId?: string;
|
||||
status?: Freight.InvoiceStatus;
|
||||
search?: string;
|
||||
tradeDirections?: string[];
|
||||
} = {},
|
||||
): Promise<Record<string, number>> {
|
||||
const qb = this.dataSource
|
||||
.getRepository(Invoice)
|
||||
.createQueryBuilder("invoice")
|
||||
.select("invoice.currency", "currency")
|
||||
.addSelect("SUM(invoice.paidAmount)", "collected")
|
||||
.groupBy("invoice.currency");
|
||||
|
||||
this.applyInvoiceFilters(qb, filter);
|
||||
|
||||
const rows: { currency: string; collected: string }[] =
|
||||
await qb.getRawMany();
|
||||
|
||||
return Object.fromEntries(
|
||||
rows.map((row) => [row.currency, Number(row.collected) || 0]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finance's offline-settlement worklist: USD invoices (paid by bank transfer,
|
||||
* never through the gateway), open ones by default or a single status when
|
||||
|
||||
Reference in New Issue
Block a user