1// Copyright (C) 2020 Storj Labs, Inc.
2// See LICENSE for copying information.
3
4package compensation
5
6import (
7	"io"
8
9	"storj.io/common/strictcsv"
10	"storj.io/storj/private/currency"
11)
12
13// Invoice holds the calculations for the amount required to pay to a node
14// for a given pay period.
15type Invoice struct {
16	Period             Period             `csv:"period"`               // The payment period
17	NodeID             NodeID             `csv:"node-id"`              // The node ID
18	NodeCreatedAt      UTCDate            `csv:"node-created-at"`      // When the node was created
19	NodeDisqualified   *UTCDate           `csv:"node-disqualified"`    // When and if the node was disqualified
20	NodeGracefulExit   *UTCDate           `csv:"node-gracefulexit"`    // When and if the node finished a graceful exit
21	NodeWallet         string             `csv:"node-wallet"`          // The node's wallet address
22	NodeWalletFeatures WalletFeatures     `csv:"node-wallet-features"` // The node's wallet features
23	NodeAddress        string             `csv:"node-address"`         // The node's TODO
24	NodeLastIP         string             `csv:"node-last-ip"`         // The last known ip the node had
25	Codes              Codes              `csv:"codes"`                // Any codes providing context to the invoice
26	UsageAtRest        float64            `csv:"usage-at-rest"`        // Byte-hours provided during the payment period
27	UsageGet           int64              `csv:"usage-get"`            // Number of bytes served in GET requests
28	UsagePut           int64              `csv:"usage-put"`            // Number of bytes served in PUT requests
29	UsageGetRepair     int64              `csv:"usage-get-repair"`     // Number of bytes served in GET_REPAIR requests
30	UsagePutRepair     int64              `csv:"usage-put-repair"`     // Number of bytes served in PUT_REPAIR requests
31	UsageGetAudit      int64              `csv:"usage-get-audit"`      // Number of bytes served in GET_AUDIT requests
32	CompAtRest         currency.MicroUnit `csv:"comp-at-rest"`         // Compensation for usage-at-rest
33	CompGet            currency.MicroUnit `csv:"comp-get"`             // Compensation for usage-get
34	CompPut            currency.MicroUnit `csv:"comp-put"`             // Compensation for usage-put
35	CompGetRepair      currency.MicroUnit `csv:"comp-get-repair"`      // Compensation for usage-get-repair
36	CompPutRepair      currency.MicroUnit `csv:"comp-put-repair"`      // Compensation for usage-put-repair
37	CompGetAudit       currency.MicroUnit `csv:"comp-get-audit"`       // Compensation for usage-get-audit
38	SurgePercent       int64              `csv:"surge-percent"`        // Surge percent used to calculate compensation, or 0 if no surge
39	Owed               currency.MicroUnit `csv:"owed"`                 // Amount we intend to pay to the node (sum(comp-*) - held + disposed)
40	Held               currency.MicroUnit `csv:"held"`                 // Amount held from sum(comp-*) for this period
41	Disposed           currency.MicroUnit `csv:"disposed"`             // Amount of owed that is due to graceful-exit or held period ending
42	TotalHeld          currency.MicroUnit `csv:"total-held"`           // Total amount ever held from the node
43	TotalDisposed      currency.MicroUnit `csv:"total-disposed"`       // Total amount ever disposed to the node
44	TotalPaid          currency.MicroUnit `csv:"total-paid"`           // Total amount ever paid to the node (but not necessarily dispensed)
45	TotalDistributed   currency.MicroUnit `csv:"total-distributed"`    // Total amount ever distributed to the node (always less than or equal to paid)
46}
47
48// MergeNodeInfo updates the fields representing the node information into the invoice.
49func (invoice *Invoice) MergeNodeInfo(nodeInfo NodeInfo) error {
50	if invoice.NodeID != NodeID(nodeInfo.ID) {
51		return Error.New("node ID mismatch (invoice=%q nodeinfo=%q)", invoice.NodeID, nodeInfo.ID)
52	}
53	invoice.NodeCreatedAt = UTCDate(nodeInfo.CreatedAt)
54	invoice.NodeDisqualified = (*UTCDate)(nodeInfo.Disqualified)
55	invoice.NodeGracefulExit = (*UTCDate)(nodeInfo.GracefulExit)
56	invoice.UsageAtRest = nodeInfo.UsageAtRest
57	invoice.UsageGet = nodeInfo.UsageGet
58	invoice.UsagePut = nodeInfo.UsagePut
59	invoice.UsageGetRepair = nodeInfo.UsageGetRepair
60	invoice.UsagePutRepair = nodeInfo.UsagePutRepair
61	invoice.UsageGetAudit = nodeInfo.UsageGetAudit
62	invoice.TotalHeld = nodeInfo.TotalHeld
63	invoice.TotalDisposed = nodeInfo.TotalDisposed
64	invoice.TotalPaid = nodeInfo.TotalPaid
65	invoice.TotalDistributed = nodeInfo.TotalDistributed
66	return nil
67}
68
69// MergeStatement updates the fields representing the calculation of the payment amounts
70// into the invoice.
71func (invoice *Invoice) MergeStatement(statement Statement) error {
72	if invoice.NodeID != NodeID(statement.NodeID) {
73		return Error.New("node ID mismatch (invoice=%q statement=%q)", invoice.NodeID, statement.NodeID)
74	}
75	invoice.Codes = statement.Codes
76	invoice.CompAtRest = statement.AtRest
77	invoice.CompGet = statement.Get
78	invoice.CompPut = statement.Put
79	invoice.CompGetRepair = statement.GetRepair
80	invoice.CompPutRepair = statement.PutRepair
81	invoice.CompGetAudit = statement.GetAudit
82	invoice.SurgePercent = statement.SurgePercent
83	invoice.Owed = statement.Owed
84	invoice.Held = statement.Held
85	invoice.Disposed = statement.Disposed
86	return nil
87}
88
89// ReadInvoices reads a collection of Invoice values in CSV form.
90func ReadInvoices(r io.Reader) ([]Invoice, error) {
91	var invoices []Invoice
92	if err := strictcsv.Read(r, &invoices); err != nil {
93		return nil, err
94	}
95	return invoices, nil
96}
97
98// WriteInvoices writes a collection of Invoice values in CSV form.
99func WriteInvoices(w io.Writer, invoices []Invoice) error {
100	return strictcsv.Write(w, invoices)
101}
102