Skip to content

Commit

Permalink
broke out recept total and tested it
Browse files Browse the repository at this point in the history
  • Loading branch information
Wholteza committed Jul 1, 2023
1 parent bc0dacf commit bd91791
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 65 deletions.
46 changes: 14 additions & 32 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import {
ReceiptRowFormModel,
ReceiptRowViewModel,
} from "./domain/receipt-row";
import { RecieptTotalInformation } from "./domain/receipt-total";
import {
RecieptTotalInformation,
RecieptTotalInformationViewModel,
calculateReceiptTotal,
toReceiptTotalViewModel,
} from "./domain/receipt-total";

const getVatTotalForItems = (
items: ReceiptRowFormModel[],
Expand Down Expand Up @@ -128,34 +133,15 @@ const App = () => {
]);
}, [currentReceiptRow, receiptFormRows, setReceiptRows]);

const receiptTotalInformation = useMemo<RecieptTotalInformation>(() => {
const vat25 = getVatTotalForItems(receiptFormRows, 25);
const vat12 = getVatTotalForItems(receiptFormRows, 12);
const vat6 = getVatTotalForItems(receiptFormRows, 6);
const vat0 = receiptFormRows
.filter((row) => row.vatPercentage === 0)
.reduce(
(total, current) =>
total + current.amount * current.pricePerPieceVatIncluded,
0
);
const totalBeforeVat = receiptFormRows.reduce(
(total, current) =>
total + current.pricePerPieceVatIncluded * current.amount,
0
);
const totalVat = vat25 + vat12 + vat6;
const receiptRows = useMemo<ReceiptRow[]>(
() => receiptFormRows.map(ReceiptRow.fromFormModel),
[receiptFormRows]
);

return {
vat25,
vat12,
vat6,
totalVatFreeAmount: vat0,
totalBeforeVat,
total: totalBeforeVat + totalVat,
totalVat,
};
}, [receiptFormRows]);
const receiptTotalInformation = useMemo<RecieptTotalInformationViewModel>(
() => toReceiptTotalViewModel(calculateReceiptTotal(receiptRows), "kr"),
[receiptRows]
);

const handleOnRemoveRow = useCallback(
(index: number) => {
Expand All @@ -166,10 +152,6 @@ const App = () => {
[receiptFormRows, setReceiptRows]
);

const receiptRows = useMemo<ReceiptRow[]>(
() => receiptFormRows.map(ReceiptRow.fromFormModel),
[receiptFormRows]
);
const handleOnClickGeneratePdf = useCallback(() => {
generatePdf(
companyInformation,
Expand Down
260 changes: 241 additions & 19 deletions src/domain/receipt-total.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { afterEach, describe, expect, jest, test } from "@jest/globals";
import * as unitModule from "./receipt-total";
import { ReceiptRow } from "./receipt-row";
import * as priceHelpers from "../helpers/price-helpers";

const generateValidReceiptRow = (): ReceiptRow => ({
amount: 1,
Expand Down Expand Up @@ -63,30 +64,75 @@ describe("calculateReceiptTotal", () => {
});

test("totalVatFreeAmount is the total from all items with vatPercentage set to 0 ", () => {
const inputThatDoesNotMatter = [generateValidReceiptRow()];
const expectedVat = 1;
const getSumOfVatFromItemsWithSpecificVatPercentageSpy = jest
.spyOn(unitModule, "getSumOfVatFromItemsWithSpecificVatPercentage")
.mockReturnValue(expectedVat);
const item1 = generateValidReceiptRow();
item1.vatPercentage = 0;
item1.amount = 5;
item1.pricePerPieceVatExcluded = 38;

const result = unitModule.calculateReceiptTotal(inputThatDoesNotMatter);
const item2 = generateValidReceiptRow();
item2.vatPercentage = 25;
item2.pricePerPieceVatExcluded = 1000;

expect(result.totalVatFreeAmount).toBe(expectedVat);
expect(
getSumOfVatFromItemsWithSpecificVatPercentageSpy
).toHaveBeenCalledWith(inputThatDoesNotMatter, 0);
});
test("total before vat is being calculated incorrectly", () => {
expect(true).toBeFalsy();
const result = unitModule.calculateReceiptTotal([item1, item2]);

expect(result.totalVatFreeAmount).toBe(
item1.pricePerPieceVatExcluded * item1.amount
);
});
test("total vat is being calculated incorrectly", () => {
expect(true).toBeFalsy();
test("total before vat is the total from all items before vat", () => {
const item1 = generateValidReceiptRow();
item1.amount = 1;
item1.pricePerPieceVatExcluded = 1;
const item2 = generateValidReceiptRow();
item2.amount = 2;
item2.pricePerPieceVatExcluded = 2;
const item3 = generateValidReceiptRow();
item3.amount = 5;
item3.pricePerPieceVatExcluded = 5;

const result = unitModule.calculateReceiptTotal([item1, item2, item3]);

expect(result.totalBeforeVat).toBe(
item1.amount * item1.pricePerPieceVatExcluded +
item2.amount * item2.pricePerPieceVatExcluded +
item3.amount * item3.pricePerPieceVatExcluded
);
});
test("different vats are being calculated incorrectly", () => {
expect(true).toBeFalsy();
test("total vat is the vat from all the items", () => {
const item1 = generateValidReceiptRow();
item1.amount = 1;
item1.vatPerPiece = 1;
const item2 = generateValidReceiptRow();
item2.amount = 2;
item2.vatPerPiece = 2;
const item3 = generateValidReceiptRow();
item3.amount = 5;
item3.vatPerPiece = 5;

const result = unitModule.calculateReceiptTotal([item1, item2, item3]);

expect(result.totalVat).toBe(
item1.amount * item1.vatPerPiece +
item2.amount * item2.vatPerPiece +
item3.amount * item3.vatPerPiece
);
});
test("logic needs to be broken out and be testable", () => {
expect(true).toBeFalsy();

test("total is the total with vat included from all the items", () => {
const item1 = generateValidReceiptRow();
item1.totalWithVatIncluded = 1;
const item2 = generateValidReceiptRow();
item2.totalWithVatIncluded = 2;
const item3 = generateValidReceiptRow();
item3.totalWithVatIncluded = 5;

const result = unitModule.calculateReceiptTotal([item1, item2, item3]);

expect(result.total).toBe(
item1.totalWithVatIncluded +
item2.totalWithVatIncluded +
item3.totalWithVatIncluded
);
});
});

Expand All @@ -112,3 +158,179 @@ describe("getSumOfVatFromItemsWithSpecificVatPercentage", () => {
expect(result).toBe(expectedVat);
});
});

const getValidReceiptTotalInformation =
(): unitModule.RecieptTotalInformation => ({
vat25: 1,
vat12: 0,
vat6: 0,
totalVatFreeAmount: 0,
totalBeforeVat: 100,
totalVat: 1,
total: 101,
});

describe("toReceiptTotalViewModel", () => {
afterEach(() => {
jest.resetAllMocks();
});
const testFacts: {
total: number;
roundedToTwoDecimalsAsString: string;
}[] = [
{
total: 25,
roundedToTwoDecimalsAsString: "25.00",
},
{
total: 25.5,
roundedToTwoDecimalsAsString: "25.50",
},
{
total: 25.55,
roundedToTwoDecimalsAsString: "25.55",
},
{
total: 25.555,
roundedToTwoDecimalsAsString: "25.56",
},
{
total: 25.545,
roundedToTwoDecimalsAsString: "25.55",
},
];

testFacts.forEach((fact) =>
test("vat25 is mapped to string with two decimals returned from helper method", () => {
const input = getValidReceiptTotalInformation();
input.vat25 = fact.total;

const toTwoDecimalStringSpy = jest
.spyOn(priceHelpers, "toTwoDecimalString")
.mockReturnValue(fact.roundedToTwoDecimalsAsString);

const result = unitModule.toReceiptTotalViewModel(input, "unit");

expect(toTwoDecimalStringSpy).toHaveBeenCalledWith(fact.total);
expect(result.vat25).toBe(fact.roundedToTwoDecimalsAsString);
})
);

testFacts.forEach((fact) =>
test("vat12 is mapped to string with two decimals returned from helper method", () => {
const input = getValidReceiptTotalInformation();
input.vat12 = fact.total;

const toTwoDecimalStringSpy = jest
.spyOn(priceHelpers, "toTwoDecimalString")
.mockReturnValue(fact.roundedToTwoDecimalsAsString);

const result = unitModule.toReceiptTotalViewModel(input, "unit");

expect(toTwoDecimalStringSpy).toHaveBeenCalledWith(fact.total);
expect(result.vat12).toBe(fact.roundedToTwoDecimalsAsString);
})
);

testFacts.forEach((fact) =>
test("vat6 is mapped to string with two decimals returned from helper method", () => {
const input = getValidReceiptTotalInformation();
input.vat6 = fact.total;

const toTwoDecimalStringSpy = jest
.spyOn(priceHelpers, "toTwoDecimalString")
.mockReturnValue(fact.roundedToTwoDecimalsAsString);

const result = unitModule.toReceiptTotalViewModel(input, "unit");

expect(toTwoDecimalStringSpy).toHaveBeenCalledWith(fact.total);
expect(result.vat6).toBe(fact.roundedToTwoDecimalsAsString);
})
);

testFacts.forEach((fact) =>
test("totalVatFreeAmount is mapped to string with two decimals returned from helper method", () => {
const input = getValidReceiptTotalInformation();
input.totalVatFreeAmount = fact.total;

const toTwoDecimalStringSpy = jest
.spyOn(priceHelpers, "toTwoDecimalString")
.mockReturnValue(fact.roundedToTwoDecimalsAsString);

const result = unitModule.toReceiptTotalViewModel(input, "unit");

expect(toTwoDecimalStringSpy).toHaveBeenCalledWith(fact.total);
expect(result.totalVatFreeAmount).toBe(fact.roundedToTwoDecimalsAsString);
})
);

testFacts.forEach((fact) =>
test("totalBeforeVat is mapped to string with two decimals returned from helper method", () => {
const input = getValidReceiptTotalInformation();
input.totalBeforeVat = fact.total;

const toTwoDecimalStringSpy = jest
.spyOn(priceHelpers, "toTwoDecimalString")
.mockReturnValue(fact.roundedToTwoDecimalsAsString);

const result = unitModule.toReceiptTotalViewModel(input, "unit");

expect(toTwoDecimalStringSpy).toHaveBeenCalledWith(fact.total);
expect(result.totalBeforeVat).toBe(fact.roundedToTwoDecimalsAsString);
})
);

testFacts.forEach((fact) =>
test("totalVat is mapped to string with two decimals returned from helper method", () => {
const input = getValidReceiptTotalInformation();
input.totalVat = fact.total;

const toTwoDecimalStringSpy = jest
.spyOn(priceHelpers, "toTwoDecimalString")
.mockReturnValue(fact.roundedToTwoDecimalsAsString);

const result = unitModule.toReceiptTotalViewModel(input, "unit");

expect(toTwoDecimalStringSpy).toHaveBeenCalledWith(fact.total);
expect(result.totalVat).toBe(fact.roundedToTwoDecimalsAsString);
})
);

testFacts.forEach((fact) =>
test("total is mapped to string with two decimals returned from helper method", () => {
const input = getValidReceiptTotalInformation();
input.total = fact.total;

const toTwoDecimalStringSpy = jest
.spyOn(priceHelpers, "toTwoDecimalString")
.mockReturnValue(fact.roundedToTwoDecimalsAsString);

const result = unitModule.toReceiptTotalViewModel(input, "unit");

expect(toTwoDecimalStringSpy).toHaveBeenCalledWith(fact.total);
expect(result.total).toBe(fact.roundedToTwoDecimalsAsString);
})
);

test("throws if unit is not set", () => {
const unit = "";
const action = () =>
unitModule.toReceiptTotalViewModel(
getValidReceiptTotalInformation(),
unit
);

expect(action).toThrow();
});

test("unit is mapped to unit property if set", () => {
const unit = "kr";

const result = unitModule.toReceiptTotalViewModel(
getValidReceiptTotalInformation(),
unit
);

expect(result.unit).toBe(unit);
});
});

0 comments on commit bd91791

Please sign in to comment.