Add invoicing billing period calculations module with sum, net amount, and view total functions; include comprehensive unit tests

This commit is contained in:
Jeppe Bundgaard
2026-03-12 16:03:22 +01:00
parent 80fcf8e4ce
commit 203123e73b
2 changed files with 140 additions and 0 deletions
@@ -0,0 +1,64 @@
const toNumber = (value) => {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : 0;
};
export const sumTransactions = (transactions = [], options = {}) => {
const { bookedOnly = false, excludeExcluded = true } = options;
return (Array.isArray(transactions) ? transactions : []).reduce((sum, transaction) => {
if (excludeExcluded && transaction?.excluded) {
return sum;
}
if (bookedOnly && !transaction?.booked) {
return sum;
}
return sum + toNumber(transaction?.amount);
}, 0);
};
export const getCustomerNetAmount = (customer = {}, options = {}, fixedPricingCustomer = null) => {
const settings = {
includeFixedPricing: false,
includeTransactions: true,
includeVehicleSubscriptions: false,
...options,
};
if (settings.includeFixedPricing) {
const fixedPrice = fixedPricingCustomer?.meta?.fixed_pricing?.price;
if (fixedPrice !== undefined && fixedPrice !== null) {
return toNumber(fixedPrice).toFixed(2);
}
}
if (settings.includeVehicleSubscriptions) {
// Placeholder for future vehicle-subscription specific logic.
}
if (settings.includeTransactions) {
return sumTransactions(customer?.transactions).toFixed(2);
}
return "0.00";
};
export const getViewTotals = (customers = []) => {
const safeCustomers = Array.isArray(customers) ? customers : [];
const total = safeCustomers.reduce((sum, customer) => {
if (customer?.meta?.fixed_pricing?.price !== undefined && customer?.meta?.fixed_pricing?.price !== null) {
return sum + toNumber(customer.meta.fixed_pricing.price);
}
return sum + sumTransactions(customer?.transactions);
}, 0);
const booked = safeCustomers.reduce((sum, customer) => {
return sum + sumTransactions(customer?.transactions, { bookedOnly: true });
}, 0);
return {
total,
booked,
notBooked: total - booked,
};
};
@@ -0,0 +1,76 @@
import { describe, expect, it } from "vitest";
import {
getCustomerNetAmount,
getViewTotals,
sumTransactions,
} from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/invoicingBillingPeriodCalculations.js";
describe("invoicing billing period calculations", () => {
it("sums transactions and excludes excluded entries by default", () => {
const sum = sumTransactions([
{ amount: 100, excluded: false },
{ amount: 50, excluded: true },
{ amount: "25.5", excluded: false },
]);
expect(sum).toBe(125.5);
});
it("supports booked-only transaction summing", () => {
const sum = sumTransactions(
[
{ amount: 100, booked: true, excluded: false },
{ amount: 30, booked: false, excluded: false },
{ amount: 20, booked: true, excluded: true },
],
{ bookedOnly: true }
);
expect(sum).toBe(100);
});
it("uses fixed pricing when requested and available", () => {
const value = getCustomerNetAmount(
{ transactions: [{ amount: 99, excluded: false }] },
{ includeFixedPricing: true, includeTransactions: true, includeVehicleSubscriptions: false },
{ meta: { fixed_pricing: { price: 250 } } }
);
expect(value).toBe("250.00");
});
it("falls back to transaction total when fixed pricing is not included", () => {
const value = getCustomerNetAmount(
{ transactions: [{ amount: 100, excluded: false }, { amount: 35.2, excluded: false }] },
{ includeFixedPricing: false, includeTransactions: true, includeVehicleSubscriptions: false },
{ meta: { fixed_pricing: { price: 999 } } }
);
expect(value).toBe("135.20");
});
it("returns zero amount when all calculation toggles are off", () => {
const value = getCustomerNetAmount(
{ transactions: [{ amount: 100, excluded: false }] },
{ includeFixedPricing: false, includeTransactions: false, includeVehicleSubscriptions: false }
);
expect(value).toBe("0.00");
});
it("computes total/booked/notBooked view totals", () => {
const totals = getViewTotals([
{
transactions: [
{ amount: 100, booked: true, excluded: false },
{ amount: 50, booked: false, excluded: false },
{ amount: 25, booked: true, excluded: true },
],
},
{
meta: { fixed_pricing: { price: 200 } },
transactions: [{ amount: 80, booked: true, excluded: false }],
},
]);
expect(totals.total).toBe(350);
expect(totals.booked).toBe(180);
expect(totals.notBooked).toBe(170);
});
});