Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cms] Invoice batch and token inventory route #1176

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 135 additions & 1 deletion politeiawww/api/cms/v1/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ server side notifications. It does not render HTML.
- [`Vote Details`](#vote-details)
- [`Active votes`](#active-votes)
- [`Start vote`](#start-vote)
- [`Proposal owners`](#proposal-owners)
- [`Invoice token inventory`](#invoice-token-inventory)
- [`Batch invoices`](#batch-invoices)
- [Error codes](#error-codes)
- [Invoice status codes](#invoice-status-codes)
- [Line item type codes](#line-item-type-codes)
Expand Down Expand Up @@ -1960,7 +1963,134 @@ Reply:
]
}
}

```

### `Invoice token inventory`

Returns the invoice tokens on the inventory separated by their invoice status.
This is a login permissioned route. If this call is done by a admin, it will
return all found tokens. If it is done by a user, it will return the tokens for
invoices in which he is the author.

**Route:** `POST /v1/invoices/tokeninventory`

**Params:**

| Parameter | Type | Description | Required |
|-----------|------|-------------|----------|
| timestampMax | int64 | Upper limit for a invoice's timestamp | |
| timestampMin | int64 | Lower limit for a invoice's timestamp | |

**Results:**

| Parameter | Type | Description |
|-|-|-|
| unreviewed | array of censhorship tokens | Tokens of invoices
with [unreviewed](#InvoiceStatusNew) status
| updated | array of censhorship tokens | Tokens of invoices
with [updated](#InvoiceStatusUpdated) status
| disputed | array of censhorship tokens | Tokens of invoices
with [disputed](#InvoiceStatusDisputed) status
| approved | array of censhorship tokens | Tokens of invoices
with [approved](#InvoiceStatusApproved) status
| paid | array of censhorship tokens | Tokens of invoices
with [paid](#InvoiceStatusPaid) status
| rejected | array of censhorship tokens | Tokens of invoices
with [rejected](#InvoiceStatusRejected) status

On failure the call shall return `400 Bad Request` and one of the following
error codes:
- [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput)

**Example**

Request:

```json
{
"timestampmax": "1588002461",
}
```

Reply:

```json
{
"unreviewed": [],
"updated": [
"337fc4762dac6bbe11d3d0130f33a09978004b190e6ebbbde9312ac63f223527"
],
"disputed": [
"e02378d40b8b9240a8ba1e53419577683f02e71edf1e4865fc54b5477b31c9c5"
],
"approved": [],
"paid": [
"2d9a58e55d17cdce496c1c8e9780828e9fdb7a727962a6ca498c91d9ceca5ebb"
],
"rejected": []
}
```

### `Batch invoices`

Returns a list of invoice records for the provided censorship tokens.

**Route:** `POST /invoices/batch`

**Params:**

| Parameter | Type | Description | Required |
|-----------|------|-------------|----------|
| tokens | array of censhorship tokens | Tokens to fetch their corresponding invoice. | |

**Results:**

| Parameter | Type | Description |
|-|-|-|
| invoices | array of invoice records | The list of invoices for the requested tokens.

On failure the call shall return `400 Bad Request` and one of the following
error codes:
- [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput)
- [`ErrorStatusMaxInvoicesExceeded`](#ErrorStatusMaxInvoicesExceeded)
- [`ErrorStatusInvalidCensorshipToken`](#ErrorStatusInvalidCensorshipToken)
- [`ErrorStatusInvoiceNotFound`](#ErrorStatusInvoiceNotFound)

**Example**

Request:

```json
{
"tokens": [
"337fc4762dac6bbe11d3d0130f33a09978004b190e6ebbbde9312ac63f223527",
]
}
```

Reply:

```json
{
"invoices": [
{
"status": 4,
"month": 12,
"year": 2018,
"timestamp": 1508296860781,
"userid": "0",
"username": "foobar",
"publickey":"5203ab0bb739f3fc267ad20c945b81bcb68ff22414510c000305f4f0afb90d1b",
"signature": "gdd92f26c8g38c90d2887259e88df614654g32fde76bef1438b0efg40e360f461e995d796g16b17108gbe226793ge4g52gg013428feb3c39de504fe5g1811e0e",
"version": "1",
"censorshiprecord": {
"token": "337fc4762dac6bbe11d3d0130f33a09978004b190e6ebbbde9312ac63f223527",
"merkle": "0dd10219cd79342198085cbe6f737bd54efe119b24c84cbc053023ed6b7da4c8",
"signature": "fcc92e26b8f38b90c2887259d88ce614654f32ecd76ade1438a0def40d360e461d995c796f16a17108fad226793fd4f52ff013428eda3b39cd504ed5f1811d0d"
}
},
]
}
```

### Error codes
Expand Down Expand Up @@ -2015,6 +2145,10 @@ Reply:
| <a name="ErrorStatusMissingSubUserIDLineItem">ErrorStatusMissingSubUserIDLineItem</a> | 1048 | Subcontractor ID cannot be blank |
| <a name="ErrorStatusInvalidSubUserIDLineItem">ErrorStatusInvalidSubUserIDLineItem</a> | 1049 | An invalid subcontractor ID was attempted to be used. |
| <a name="ErrorStatusInvalidSupervisorUser">ErrorStatusInvalidSupervisorUser</a> | 1050 | An invalid Supervisor User ID was attempted to be used. |
| <a name="ErrorStatusMalformedDCC">ErrorStatusMalformedDCC</a> | 1051 | A malformed DCC was attempted to be used. |
| <a name="ErrorStatusMaxInvoicesExceeded">ErrorStatusMaxInvoicesExceeded</a> | 1052 | Number of invoices requested exceeded the InvoiceListPageSize. |
| <a name="ErrorStatusMalformedDCC">ErrorStatusMalformedDCC</a> | 1051 | A malformed DCC was attempted to be used. |
| <a name="ErrorStatusMaxInvoicesExceeded">ErrorStatusMaxInvoicesExceeded</a> | 1052 | Number of invoices requested exceeded the InvoiceListPageSize. |

### Invoice status codes

Expand Down
32 changes: 32 additions & 0 deletions politeiawww/api/cms/v1/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const (
RouteProposalBilling = "/proposals/billing"
RouteProposalBillingSummary = "/proposals/spendingsummary"
RouteProposalBillingDetails = "/proposals/spendingdetails"
RouteInvoiceTokenInventory = "/invoices/tokeninventory"
RouteBatchInvoices = "/invoices/batch"

// Invoice status codes
InvoiceStatusInvalid InvoiceStatusT = 0 // Invalid status
Expand Down Expand Up @@ -241,6 +243,7 @@ const (
ErrorStatusDCCVoteEnded www.ErrorStatusT = 1054
ErrorStatusDCCVoteStillLive www.ErrorStatusT = 1055
ErrorStatusDCCDuplicateVote www.ErrorStatusT = 1056
ErrorStatusMaxInvoicesExceeded www.ErrorStatusT = 1057

ProposalsMainnet = "https://proposals.decred.org"
ProposalsTestnet = "https://test-proposals.decred.org"
Expand Down Expand Up @@ -1044,3 +1047,32 @@ type ProposalBillingDetails struct {
type ProposalBillingDetailsReply struct {
Details ProposalSpending `json:"details"`
}

// InvoiceTokenInventory specifies upper and lower timestamp bounds for the
// retrieval of invoice tokens.
type InvoiceTokenInventory struct {
TimestampMax int64 `json:"timestampmax"`
TimestampMin int64 `json:"timestampmin"`
}

// InvoiceTokenInventoryReply returns all invoice tokens from the inventory,
// separated by status.
type InvoiceTokenInventoryReply struct {
Unreviewed []string `json:"unreviewed"`
Updated []string `json:"updated"`
Disputed []string `json:"disputed"`
Approved []string `json:"approved"`
Paid []string `json:"paid"`
Rejected []string `json:"rejected"`
}

// BatchInvoices specifies a list of tokens to fetch their corresponding
// invoices.
type BatchInvoices struct {
Tokens []string `json:"tokens"`
}

// BatchInvoicesReply returns the invoices for the provided token list.
type BatchInvoicesReply struct {
Invoices []InvoiceRecord `json:"invoices"`
}
109 changes: 109 additions & 0 deletions politeiawww/cmd/cmswww/batchinvoices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) 2017-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main

import (
"fmt"

v1 "github.com/decred/politeia/politeiawww/api/cms/v1"
"github.com/decred/politeia/politeiawww/cmd/shared"
)

// BatchInvoicesCmd retrieves a set of invoices.
type BatchInvoicesCmd struct{}

// Execute executes the batch invoices command.
func (cmd *BatchInvoicesCmd) Execute(args []string) error {
// Get server's public key
vr, err := client.Version()
if err != nil {
return err
}

// Make batch invoices call
bir, err := client.BatchInvoices(&v1.BatchInvoices{
Tokens: args,
})
if err != nil {
return err
}

// Verify invoice censorship records from reply
for _, i := range bir.Invoices {
err = verifyInvoice(i, vr.PubKey)
if err != nil {
return fmt.Errorf("unable to verify invoice %v: %v",
i.CensorshipRecord.Token, err)
}
}

return shared.PrintJSON(bir)
}

// batchInvoicesHelpMsg is the output of the help command when 'batchinvoices' is specified.
const batchInvoicesHelpMsg = `batchinvoices

Fetch a list of invoices.

Arguments: Tokens

Example (Admin/User):
batchinvoices token1 token2 token3 ...

Result:
{
"invoices": [
{
"status": (int) Current status of invoice
"timestamp": (int64) Last update of invoice
"userid": (string) UUID of invoice author
"username": (string) Username of invoice author
"publickey": (string) Author's public key, used to verify signature
"signature": (string) Signature of file digest
"file": (File) Invoice csv file
"version": (string) Record version
"input": {
"version": (uint) Version of the invoice input
"month": (uint) Month of invoice
"year": (uint) Year of invoice
"exchangerate": (uint) Exchange rate of a given month/year in USD cents
"contractorname": (string) Real name of the contractor
"contractorlocation": (string) Real location of the contractor
"contractorcontact": (string) Contact of the contractor
"contractorrate": (uint) Contractor pay rate in USD cents
"paymentaddress": (string) Decred payment address
"lineitems": [
{
"type": (int) Type of work performed
"domain": (string) Domain of work performed
"subdomain": (string) Subdomain of work performed
"description": (string) Description of work performed
"proposaltoken": (string) Proposal token that work is associated with
"subuserid": (string) User ID of the associated subcontractor
"subrate": (uint) Payrate of the subcontractor
"labor": (uint) Number of minutes (if type is labor)
"expenses": (uint) Total cost in USD cents (if type is expense or misc)
}
]
},
"payment": {
"token": (string) Payment token
"address": (string) Payment address
"txids": ([]string) TxIds associated with this invoice payment
"timestarted": (int64) Time when payment started
"timelastupdated": (int64) Time when invoice was last updated
"amountneeded": (int64) Amount of decreds needed for payment
"amountreceived": (int64) Amount of decreds received from payment
"status": (int) Payment status code
},
"censorshiprecord": {
"token": (string) Invoice censorship token
"merkle": (string) Merkle root of invoice
"signature": (string) Server-side signature
}
}
]
}
`
2 changes: 2 additions & 0 deletions politeiawww/cmd/cmswww/cmswww.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type cmswww struct {
// Commands
ActiveVotes ActiveVotesCmd `command:"activevotes" description:"(user) get the dccs that are being voted on"`
BatchProposals shared.BatchProposalsCmd `command:"batchproposals" description:"(user) retrieve a set of proposals"`
BatchInvoices BatchInvoicesCmd `command:"batchinvoices" description:"(user/admin) get invoices by list of record tokens"`
CensorComment shared.CensorCommentCmd `command:"censorcomment" description:"(admin) censor a comment"`
ChangePassword shared.ChangePasswordCmd `command:"changepassword" description:"(user) change the password for the logged in user"`
ChangeUsername shared.ChangeUsernameCmd `command:"changeusername" description:"(user) change the username for the logged in user"`
Expand Down Expand Up @@ -106,6 +107,7 @@ type cmswww struct {
Version shared.VersionCmd `command:"version" description:"(public) get server info and CSRF token"`
VoteDCC VoteDCCCmd `command:"votedcc" description:"(user) vote for a given DCC during an all contractor vote"`
VoteDetails VoteDetailsCmd `command:"votedetails" description:"(user) get the details for a dcc vote"`
InvoiceTokenInventory InvoiceTokenInventoryCmd `command:"invoicetokeninventory" description:"(user/admin) get list of invoice tokens by status"`
}

// verifyInvoice verifies a invoice's merkle root, author signature, and
Expand Down
5 changes: 4 additions & 1 deletion politeiawww/cmd/cmswww/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ func (cmd *HelpCmd) Execute(args []string) error {
fmt.Printf("%s\n", dccCommentsHelpMsg)
case "newdcccomment":
fmt.Printf("%s\n", newDCCCommentHelpMsg)

case "batchinvoices":
fmt.Printf("%s\n", batchInvoicesHelpMsg)
case "tokeninventory":
fmt.Printf("%s\n", invoiceTokenInventoryHelpMsg)
default:
fmt.Printf("invalid command: use 'cmswww -h' " +
"to view a list of valid commands\n")
Expand Down
Loading