forked from hswaw/hscloud
invoice: calculate GTU codes for invoice, implement some tests
Also drive-by fix two proto issues: - rename gtu_codes to gtu_code (following convention) - move denormalized Item.due_date field past denormalized comment. Change-Id: Ibfe0a21aadc0a5d4e2f784b182e530b9603aae62
This commit is contained in:
parent
d67635d338
commit
b456c18bb2
5 changed files with 160 additions and 3 deletions
|
@ -1,4 +1,4 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
|
@ -31,3 +31,9 @@ go_binary(
|
|||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["calc_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
pb "code.hackerspace.pl/hscloud/bgpwtf/invoice/proto"
|
||||
)
|
||||
|
||||
// calculateInvoiceData applies all business logic to populate an Invoice's
|
||||
// denormalized fields from its InvoiceData.
|
||||
func calculateInvoiceData(p *pb.Invoice) {
|
||||
// Populate default unit.
|
||||
// TODO(q3k): this really should be done on invoice submit instead.
|
||||
p.Unit = p.Data.Unit
|
||||
if p.Unit == "" {
|
||||
p.Unit = "€"
|
||||
}
|
||||
|
||||
// Calculate totals.
|
||||
p.TotalNet = 0
|
||||
p.Total = 0
|
||||
for _, i := range p.Data.Item {
|
||||
|
@ -24,6 +30,21 @@ func calculateInvoiceData(p *pb.Invoice) {
|
|||
i.Total = rowTotal
|
||||
}
|
||||
|
||||
// Calculate due date.
|
||||
due := int64(time.Hour*24) * p.Data.DaysDue
|
||||
p.DueDate = time.Unix(0, p.Date).Add(time.Duration(due)).UnixNano()
|
||||
|
||||
// Denormalize Items' GTUCodes into the Invoice's summary GTU codes.
|
||||
codeSet := make(map[pb.GTUCode]bool)
|
||||
for _, item := range p.Data.Item {
|
||||
for _, code := range item.GtuCode {
|
||||
codeSet[code] = true
|
||||
}
|
||||
}
|
||||
var codes []pb.GTUCode
|
||||
for c, _ := range codeSet {
|
||||
codes = append(codes, c)
|
||||
}
|
||||
sort.Slice(codes, func(i, j int) bool { return codes[i] < codes[j] })
|
||||
p.GtuCode = codes
|
||||
}
|
||||
|
|
129
bgpwtf/invoice/calc_test.go
Normal file
129
bgpwtf/invoice/calc_test.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
pb "code.hackerspace.pl/hscloud/bgpwtf/invoice/proto"
|
||||
)
|
||||
|
||||
// Fake test data for test in this file.
|
||||
var (
|
||||
itemInternet1 = &pb.Item{
|
||||
Title: "Dostęp do Internetu - Umowa FOOBAR/10 - Opłata Abonentowa 2020/08",
|
||||
Count: 1,
|
||||
UnitPrice: 4200,
|
||||
Vat: 23000,
|
||||
}
|
||||
itemInternet2 = &pb.Item{
|
||||
Title: "Dostęp do Internetu - Umowa FOOBAR/10 - Opłata Abonentowa 2020/09",
|
||||
Count: 1,
|
||||
UnitPrice: 4200,
|
||||
Vat: 23000,
|
||||
}
|
||||
itemHardware = &pb.Item{
|
||||
Title: "Thinkpad x230, i7, 16GB RAM, Refurbished",
|
||||
Count: 1,
|
||||
UnitPrice: 10000,
|
||||
Vat: 23000,
|
||||
GtuCode: []pb.GTUCode{pb.GTUCode_GTU_05},
|
||||
}
|
||||
billing1 = []string{
|
||||
"Wykop Sp. z o. o.",
|
||||
"Zakręt 8",
|
||||
"60-351 Poznań",
|
||||
}
|
||||
billing2 = []string{
|
||||
"TEH Adam Karolczak",
|
||||
"Zgoda 18/2",
|
||||
"95-200 Pabianice",
|
||||
}
|
||||
vatID1 = "PL8086133742"
|
||||
vatID2 = "DE133742429"
|
||||
iban = "PL 59 1090 2402 9746 7956 2256 2375"
|
||||
swift = "WLPPZLPAXXX"
|
||||
)
|
||||
|
||||
func TestCalculate(t *testing.T) {
|
||||
now := time.Now()
|
||||
for _, te := range []struct {
|
||||
description string
|
||||
data *pb.InvoiceData
|
||||
want *pb.Invoice
|
||||
}{
|
||||
{
|
||||
description: "Invoice without JPK_V7 codes",
|
||||
data: &pb.InvoiceData{
|
||||
Item: []*pb.Item{itemInternet1, itemInternet2},
|
||||
InvoicerBilling: billing1,
|
||||
CustomerBilling: billing2,
|
||||
InvoicerVatId: vatID1,
|
||||
CustomerVatId: vatID2,
|
||||
Date: now.UnixNano(),
|
||||
DaysDue: 21,
|
||||
Iban: iban,
|
||||
Swift: swift,
|
||||
Unit: "PLN",
|
||||
},
|
||||
want: &pb.Invoice{
|
||||
TotalNet: 8400,
|
||||
Total: 10332,
|
||||
Unit: "PLN",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Invoice with JPK_V7 codes",
|
||||
data: &pb.InvoiceData{
|
||||
// Repeated item with GTU code GTU_5, to ensure result doesn't
|
||||
// have repeated codes.
|
||||
Item: []*pb.Item{itemInternet1, itemHardware, itemHardware},
|
||||
InvoicerBilling: billing1,
|
||||
CustomerBilling: billing2,
|
||||
InvoicerVatId: vatID1,
|
||||
CustomerVatId: vatID2,
|
||||
Date: now.UnixNano(),
|
||||
DaysDue: 21,
|
||||
Iban: iban,
|
||||
Swift: swift,
|
||||
Unit: "PLN",
|
||||
},
|
||||
want: &pb.Invoice{
|
||||
TotalNet: 24200,
|
||||
Total: 29766,
|
||||
Unit: "PLN",
|
||||
GtuCode: []pb.GTUCode{pb.GTUCode_GTU_05},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(te.description, func(t *testing.T) {
|
||||
invoice := &pb.Invoice{
|
||||
Data: te.data,
|
||||
Date: te.data.Date,
|
||||
}
|
||||
calculateInvoiceData(invoice)
|
||||
if want, got := te.want.TotalNet, invoice.TotalNet; want != got {
|
||||
t.Errorf("got TotalNet %d, wanted %d", got, want)
|
||||
}
|
||||
if want, got := te.want.Total, invoice.Total; want != got {
|
||||
t.Errorf("got Total %d, wanted %d", got, want)
|
||||
}
|
||||
if want, got := te.want.Unit, invoice.Unit; want != got {
|
||||
t.Errorf("got Unit %q, wanted %q", got, want)
|
||||
}
|
||||
due := time.Duration(int64(time.Hour*24) * te.data.DaysDue)
|
||||
if want, got := now.Add(due).UnixNano(), invoice.DueDate; want != got {
|
||||
t.Errorf("got DueDate %d, wanted %d", got, want)
|
||||
}
|
||||
if want, got := len(te.want.GtuCode), len(invoice.GtuCode); want != got {
|
||||
t.Errorf("got %d GTU codes, wanted %d", got, want)
|
||||
} else {
|
||||
for i, want := range te.want.GtuCode {
|
||||
got := invoice.GtuCode[i]
|
||||
if want != got {
|
||||
t.Errorf("GTU code %d: wanted %s, got %s", i, want.String(), got.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
load("@rules_proto//proto:defs.bzl", "proto_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
|
||||
|
||||
|
|
|
@ -264,12 +264,12 @@ message Invoice {
|
|||
// If sealed, otherwise 'proforma'.
|
||||
string final_uid = 3;
|
||||
int64 date = 4;
|
||||
int64 due_date = 5;
|
||||
// Denormalized fields follow.
|
||||
int64 due_date = 5;
|
||||
uint64 total_net = 6;
|
||||
uint64 total = 7;
|
||||
string unit = 8;
|
||||
repeated GTUCode gtu_codes = 10;
|
||||
repeated GTUCode gtu_code = 10;
|
||||
// Next tag: 11;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue