Add views to compute taxes and total amount of invoices
They are not functions because i need to join them with the main invoice relation, and although possible is a bit more awkward with functions. The taxes have their own relation because i will need them grouped by their name in the PDF, so it will probably be a select for that relation.
This commit is contained in:
parent
32fdab4217
commit
97ef02b0f9
|
@ -0,0 +1,32 @@
|
|||
-- Deploy numerus:invoice_amount to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: invoice_product
|
||||
-- requires: invoice_tax_amount
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace view invoice_amount as
|
||||
with taxable as (
|
||||
select invoice_id
|
||||
, sum(round(price * quantity * (1 - discount_rate))::integer)::integer as subtotal
|
||||
from invoice_product
|
||||
group by invoice_id
|
||||
), taxes as (
|
||||
select invoice_id
|
||||
, sum(amount)::integer as tax_amount
|
||||
from invoice_tax_amount
|
||||
group by invoice_id
|
||||
)
|
||||
select invoice_id
|
||||
, subtotal
|
||||
, subtotal + coalesce(tax_amount, 0) as total
|
||||
from taxable
|
||||
left join taxes using (invoice_id)
|
||||
;
|
||||
|
||||
grant select on table invoice_amount to invoicer;
|
||||
grant select on table invoice_amount to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Deploy numerus:invoice_tax_amount to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: invoice_product
|
||||
-- requires: invoice_product_tax
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace view invoice_tax_amount as
|
||||
select invoice_id
|
||||
, tax_id
|
||||
, sum(round(round(price * quantity * (1 - discount_rate))::integer * tax_rate)::integer)::integer as amount
|
||||
from invoice_product
|
||||
join invoice_product_tax using (invoice_product_id)
|
||||
group by invoice_id
|
||||
, tax_id
|
||||
;
|
||||
|
||||
grant select on table invoice_tax_amount to invoicer;
|
||||
grant select on table invoice_tax_amount to admin;
|
||||
|
||||
commit;
|
|
@ -15,6 +15,7 @@ type InvoiceEntry struct {
|
|||
Slug string
|
||||
Date time.Time
|
||||
Number string
|
||||
Total string
|
||||
CustomerName string
|
||||
CustomerSlug string
|
||||
Status string
|
||||
|
@ -33,13 +34,13 @@ func IndexInvoices(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
|||
}
|
||||
|
||||
func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company, locale *Locale) []*InvoiceEntry {
|
||||
rows := conn.MustQuery(ctx, "select invoice.slug, invoice_date, invoice_number, contact.business_name, contact.slug, invoice.invoice_status, isi18n.name from invoice join contact using (contact_id) join invoice_status_i18n isi18n on invoice.invoice_status = isi18n.invoice_status and isi18n.lang_tag = $2 where invoice.company_id = $1 order by invoice_date, invoice_number", company.Id, locale.Language.String())
|
||||
rows := conn.MustQuery(ctx, "select invoice.slug, invoice_date, invoice_number, contact.business_name, contact.slug, invoice.invoice_status, isi18n.name, to_price(total, decimal_digits) from invoice join contact using (contact_id) join invoice_status_i18n isi18n on invoice.invoice_status = isi18n.invoice_status and isi18n.lang_tag = $2 join invoice_amount using (invoice_id) join currency using (currency_code) where invoice.company_id = $1 order by invoice_date, invoice_number", company.Id, locale.Language.String())
|
||||
defer rows.Close()
|
||||
|
||||
var entries []*InvoiceEntry
|
||||
for rows.Next() {
|
||||
entry := &InvoiceEntry{}
|
||||
if err := rows.Scan(&entry.Slug, &entry.Date, &entry.Number, &entry.CustomerName, &entry.CustomerSlug, &entry.Status, &entry.StatusLabel); err != nil {
|
||||
if err := rows.Scan(&entry.Slug, &entry.Date, &entry.Number, &entry.CustomerName, &entry.CustomerSlug, &entry.Status, &entry.StatusLabel, &entry.Total); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
|
|
113
po/ca.po
113
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-02-12 20:51+0100\n"
|
||||
"POT-Creation-Date: 2023-02-22 14:35+0100\n"
|
||||
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||
|
@ -68,17 +68,17 @@ msgstr "Preu"
|
|||
msgid "No products added yet."
|
||||
msgstr "No hi ha cap producte."
|
||||
|
||||
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:37
|
||||
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:38
|
||||
msgctxt "action"
|
||||
msgid "Add products"
|
||||
msgstr "Afegeix productes"
|
||||
|
||||
#: web/template/invoices/new.gohtml:38
|
||||
#: web/template/invoices/new.gohtml:40
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
msgstr "Actualitza"
|
||||
|
||||
#: web/template/invoices/new.gohtml:40 web/template/invoices/index.gohtml:13
|
||||
#: web/template/invoices/new.gohtml:42 web/template/invoices/index.gohtml:13
|
||||
msgctxt "action"
|
||||
msgid "New invoice"
|
||||
msgstr "Nova factura"
|
||||
|
@ -115,10 +115,15 @@ msgstr "Etiqueta"
|
|||
|
||||
#: web/template/invoices/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: web/template/invoices/index.gohtml:28
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descàrrega"
|
||||
|
||||
#: web/template/invoices/index.gohtml:45
|
||||
#: web/template/invoices/index.gohtml:47
|
||||
msgid "No invoices added yet."
|
||||
msgstr "No hi ha cap factura."
|
||||
|
||||
|
@ -306,7 +311,7 @@ msgctxt "action"
|
|||
msgid "Update product"
|
||||
msgstr "Actualitza producte"
|
||||
|
||||
#: pkg/login.go:36 pkg/profile.go:40 pkg/contacts.go:178
|
||||
#: pkg/login.go:36 pkg/profile.go:40 pkg/contacts.go:172
|
||||
msgctxt "input"
|
||||
msgid "Email"
|
||||
msgstr "Correu-e"
|
||||
|
@ -316,11 +321,11 @@ msgctxt "input"
|
|||
msgid "Password"
|
||||
msgstr "Contrasenya"
|
||||
|
||||
#: pkg/login.go:69 pkg/profile.go:89 pkg/contacts.go:269
|
||||
#: pkg/login.go:69 pkg/profile.go:89 pkg/contacts.go:263
|
||||
msgid "Email can not be empty."
|
||||
msgstr "No podeu deixar el correu-e en blanc."
|
||||
|
||||
#: pkg/login.go:70 pkg/profile.go:90 pkg/contacts.go:270
|
||||
#: pkg/login.go:70 pkg/profile.go:90 pkg/contacts.go:264
|
||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
|
||||
|
||||
|
@ -332,40 +337,40 @@ msgstr "No podeu deixar la contrasenya en blanc."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||
|
||||
#: pkg/products.go:194 pkg/invoices.go:321
|
||||
#: pkg/products.go:165 pkg/invoices.go:304
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: pkg/products.go:200 pkg/invoices.go:327
|
||||
#: pkg/products.go:171 pkg/invoices.go:309
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripció"
|
||||
|
||||
#: pkg/products.go:205 pkg/invoices.go:332
|
||||
#: pkg/products.go:176 pkg/invoices.go:313
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
||||
#: pkg/products.go:215 pkg/invoices.go:237 pkg/invoices.go:361
|
||||
#: pkg/products.go:186 pkg/invoices.go:219 pkg/invoices.go:339
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Imposts"
|
||||
|
||||
#: pkg/products.go:235 pkg/profile.go:92 pkg/invoices.go:267
|
||||
#: pkg/invoices.go:384
|
||||
#: pkg/products.go:206 pkg/profile.go:92 pkg/invoices.go:252
|
||||
#: pkg/invoices.go:375
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podeu deixar el nom en blanc."
|
||||
|
||||
#: pkg/products.go:236 pkg/invoices.go:385
|
||||
#: pkg/products.go:207 pkg/invoices.go:376
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podeu deixar el preu en blanc."
|
||||
|
||||
#: pkg/products.go:237 pkg/invoices.go:386
|
||||
#: pkg/products.go:208 pkg/invoices.go:377
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El preu ha de ser un número major a zero."
|
||||
|
||||
#: pkg/products.go:239 pkg/invoices.go:271 pkg/invoices.go:394
|
||||
#: pkg/products.go:210 pkg/invoices.go:256 pkg/invoices.go:385
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Heu seleccionat un impost que no és vàlid."
|
||||
|
||||
|
@ -428,168 +433,168 @@ msgstr "La confirmació no és igual a la contrasenya."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "Heu seleccionat un idioma que no és vàlid."
|
||||
|
||||
#: pkg/invoices.go:66
|
||||
#: pkg/invoices.go:71
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escolliu un client a facturar."
|
||||
|
||||
#: pkg/invoices.go:163
|
||||
#: pkg/invoices.go:145
|
||||
msgid "Invalid action"
|
||||
msgstr "Acció invàlida."
|
||||
|
||||
#: pkg/invoices.go:214
|
||||
#: pkg/invoices.go:196
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: pkg/invoices.go:220
|
||||
#: pkg/invoices.go:202
|
||||
msgctxt "input"
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: pkg/invoices.go:226
|
||||
#: pkg/invoices.go:208
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Data de factura"
|
||||
|
||||
#: pkg/invoices.go:232
|
||||
#: pkg/invoices.go:214
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notes"
|
||||
|
||||
#: pkg/invoices.go:268
|
||||
#: pkg/invoices.go:253
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podeu deixar la data de la factura en blanc."
|
||||
|
||||
#: pkg/invoices.go:269
|
||||
#: pkg/invoices.go:254
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La data de facturació ha de ser vàlida."
|
||||
|
||||
#: pkg/invoices.go:315
|
||||
#: pkg/invoices.go:299
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/invoices.go:342
|
||||
#: pkg/invoices.go:322
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantitat"
|
||||
|
||||
#: pkg/invoices.go:351
|
||||
#: pkg/invoices.go:330
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descompte (%)"
|
||||
|
||||
#: pkg/invoices.go:388
|
||||
#: pkg/invoices.go:379
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podeu deixar la quantitat en blanc."
|
||||
|
||||
#: pkg/invoices.go:389
|
||||
#: pkg/invoices.go:380
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La quantitat ha de ser un número major a zero."
|
||||
|
||||
#: pkg/invoices.go:391
|
||||
#: pkg/invoices.go:382
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podeu deixar el descompte en blanc."
|
||||
|
||||
#: pkg/invoices.go:392
|
||||
#: pkg/invoices.go:383
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
|
||||
|
||||
#: pkg/contacts.go:149
|
||||
#: pkg/contacts.go:143
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nom i cognoms"
|
||||
|
||||
#: pkg/contacts.go:158
|
||||
#: pkg/contacts.go:152
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: pkg/contacts.go:164
|
||||
#: pkg/contacts.go:158
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nom comercial"
|
||||
|
||||
#: pkg/contacts.go:169
|
||||
#: pkg/contacts.go:163
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Telèfon"
|
||||
|
||||
#: pkg/contacts.go:187
|
||||
#: pkg/contacts.go:181
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: pkg/contacts.go:195
|
||||
#: pkg/contacts.go:189
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Adreça"
|
||||
|
||||
#: pkg/contacts.go:204
|
||||
#: pkg/contacts.go:198
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Població"
|
||||
|
||||
#: pkg/contacts.go:210
|
||||
#: pkg/contacts.go:204
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Província"
|
||||
|
||||
#: pkg/contacts.go:216
|
||||
#: pkg/contacts.go:210
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Codi postal"
|
||||
|
||||
#: pkg/contacts.go:225
|
||||
#: pkg/contacts.go:219
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: pkg/contacts.go:258
|
||||
#: pkg/contacts.go:252
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Heu seleccionat un país que no és vàlid."
|
||||
|
||||
#: pkg/contacts.go:262
|
||||
#: pkg/contacts.go:256
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podeu deixar el nom i els cognoms en blanc."
|
||||
|
||||
#: pkg/contacts.go:263
|
||||
#: pkg/contacts.go:257
|
||||
msgid "VAT number can not be empty."
|
||||
msgstr "No podeu deixar el DNI o NIF en blanc."
|
||||
|
||||
#: pkg/contacts.go:264
|
||||
#: pkg/contacts.go:258
|
||||
msgid "This value is not a valid VAT number."
|
||||
msgstr "Aquest valor no és un DNI o NIF vàlid."
|
||||
|
||||
#: pkg/contacts.go:266
|
||||
#: pkg/contacts.go:260
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podeu deixar el telèfon en blanc."
|
||||
|
||||
#: pkg/contacts.go:267
|
||||
#: pkg/contacts.go:261
|
||||
msgid "This value is not a valid phone number."
|
||||
msgstr "Aquest valor no és un telèfon vàlid."
|
||||
|
||||
#: pkg/contacts.go:273
|
||||
#: pkg/contacts.go:267
|
||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||
msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/."
|
||||
|
||||
#: pkg/contacts.go:275
|
||||
#: pkg/contacts.go:269
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podeu deixar l’adreça en blanc."
|
||||
|
||||
#: pkg/contacts.go:276
|
||||
#: pkg/contacts.go:270
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podeu deixar la població en blanc."
|
||||
|
||||
#: pkg/contacts.go:277
|
||||
#: pkg/contacts.go:271
|
||||
msgid "Province can not be empty."
|
||||
msgstr "No podeu deixar la província en blanc."
|
||||
|
||||
#: pkg/contacts.go:278
|
||||
#: pkg/contacts.go:272
|
||||
msgid "Postal code can not be empty."
|
||||
msgstr "No podeu deixar el codi postal en blanc."
|
||||
|
||||
#: pkg/contacts.go:279
|
||||
#: pkg/contacts.go:273
|
||||
msgid "This value is not a valid postal code."
|
||||
msgstr "Aquest valor no és un codi postal vàlid."
|
||||
|
||||
|
|
113
po/es.po
113
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-02-12 20:51+0100\n"
|
||||
"POT-Creation-Date: 2023-02-22 14:35+0100\n"
|
||||
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
|
@ -68,17 +68,17 @@ msgstr "Precio"
|
|||
msgid "No products added yet."
|
||||
msgstr "No hay productos."
|
||||
|
||||
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:37
|
||||
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:38
|
||||
msgctxt "action"
|
||||
msgid "Add products"
|
||||
msgstr "Añadir productos"
|
||||
|
||||
#: web/template/invoices/new.gohtml:38
|
||||
#: web/template/invoices/new.gohtml:40
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
msgstr "Actualizar"
|
||||
|
||||
#: web/template/invoices/new.gohtml:40 web/template/invoices/index.gohtml:13
|
||||
#: web/template/invoices/new.gohtml:42 web/template/invoices/index.gohtml:13
|
||||
msgctxt "action"
|
||||
msgid "New invoice"
|
||||
msgstr "Nueva factura"
|
||||
|
@ -115,10 +115,15 @@ msgstr "Etiqueta"
|
|||
|
||||
#: web/template/invoices/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Importe"
|
||||
|
||||
#: web/template/invoices/index.gohtml:28
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descargar"
|
||||
|
||||
#: web/template/invoices/index.gohtml:45
|
||||
#: web/template/invoices/index.gohtml:47
|
||||
msgid "No invoices added yet."
|
||||
msgstr "No hay facturas."
|
||||
|
||||
|
@ -306,7 +311,7 @@ msgctxt "action"
|
|||
msgid "Update product"
|
||||
msgstr "Actualizar producto"
|
||||
|
||||
#: pkg/login.go:36 pkg/profile.go:40 pkg/contacts.go:178
|
||||
#: pkg/login.go:36 pkg/profile.go:40 pkg/contacts.go:172
|
||||
msgctxt "input"
|
||||
msgid "Email"
|
||||
msgstr "Correo-e"
|
||||
|
@ -316,11 +321,11 @@ msgctxt "input"
|
|||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
||||
#: pkg/login.go:69 pkg/profile.go:89 pkg/contacts.go:269
|
||||
#: pkg/login.go:69 pkg/profile.go:89 pkg/contacts.go:263
|
||||
msgid "Email can not be empty."
|
||||
msgstr "No podéis dejar el correo-e en blanco."
|
||||
|
||||
#: pkg/login.go:70 pkg/profile.go:90 pkg/contacts.go:270
|
||||
#: pkg/login.go:70 pkg/profile.go:90 pkg/contacts.go:264
|
||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
|
||||
|
||||
|
@ -332,40 +337,40 @@ msgstr "No podéis dejar la contraseña en blanco."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nombre de usuario o contraseña inválido."
|
||||
|
||||
#: pkg/products.go:194 pkg/invoices.go:321
|
||||
#: pkg/products.go:165 pkg/invoices.go:304
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: pkg/products.go:200 pkg/invoices.go:327
|
||||
#: pkg/products.go:171 pkg/invoices.go:309
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: pkg/products.go:205 pkg/invoices.go:332
|
||||
#: pkg/products.go:176 pkg/invoices.go:313
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Precio"
|
||||
|
||||
#: pkg/products.go:215 pkg/invoices.go:237 pkg/invoices.go:361
|
||||
#: pkg/products.go:186 pkg/invoices.go:219 pkg/invoices.go:339
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Impuestos"
|
||||
|
||||
#: pkg/products.go:235 pkg/profile.go:92 pkg/invoices.go:267
|
||||
#: pkg/invoices.go:384
|
||||
#: pkg/products.go:206 pkg/profile.go:92 pkg/invoices.go:252
|
||||
#: pkg/invoices.go:375
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podéis dejar el nombre en blanco."
|
||||
|
||||
#: pkg/products.go:236 pkg/invoices.go:385
|
||||
#: pkg/products.go:207 pkg/invoices.go:376
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podéis dejar el precio en blanco."
|
||||
|
||||
#: pkg/products.go:237 pkg/invoices.go:386
|
||||
#: pkg/products.go:208 pkg/invoices.go:377
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El precio tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/products.go:239 pkg/invoices.go:271 pkg/invoices.go:394
|
||||
#: pkg/products.go:210 pkg/invoices.go:256 pkg/invoices.go:385
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Habéis escogido un impuesto que no es válido."
|
||||
|
||||
|
@ -428,168 +433,168 @@ msgstr "La confirmación no corresponde con la contraseña."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "Habéis escogido un idioma que no es válido."
|
||||
|
||||
#: pkg/invoices.go:66
|
||||
#: pkg/invoices.go:71
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escoged un cliente a facturar."
|
||||
|
||||
#: pkg/invoices.go:163
|
||||
#: pkg/invoices.go:145
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción inválida."
|
||||
|
||||
#: pkg/invoices.go:214
|
||||
#: pkg/invoices.go:196
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Cliente"
|
||||
|
||||
#: pkg/invoices.go:220
|
||||
#: pkg/invoices.go:202
|
||||
msgctxt "input"
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: pkg/invoices.go:226
|
||||
#: pkg/invoices.go:208
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Fecha de factura"
|
||||
|
||||
#: pkg/invoices.go:232
|
||||
#: pkg/invoices.go:214
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notas"
|
||||
|
||||
#: pkg/invoices.go:268
|
||||
#: pkg/invoices.go:253
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podéis dejar la fecha de la factura en blanco."
|
||||
|
||||
#: pkg/invoices.go:269
|
||||
#: pkg/invoices.go:254
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La fecha de factura debe ser válida."
|
||||
|
||||
#: pkg/invoices.go:315
|
||||
#: pkg/invoices.go:299
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/invoices.go:342
|
||||
#: pkg/invoices.go:322
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Cantidad"
|
||||
|
||||
#: pkg/invoices.go:351
|
||||
#: pkg/invoices.go:330
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descuento (%)"
|
||||
|
||||
#: pkg/invoices.go:388
|
||||
#: pkg/invoices.go:379
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podéis dejar la cantidad en blanco."
|
||||
|
||||
#: pkg/invoices.go:389
|
||||
#: pkg/invoices.go:380
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La cantidad tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/invoices.go:391
|
||||
#: pkg/invoices.go:382
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podéis dejar el descuento en blanco."
|
||||
|
||||
#: pkg/invoices.go:392
|
||||
#: pkg/invoices.go:383
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descuento tiene que ser un percentage entre 0 y 100."
|
||||
|
||||
#: pkg/contacts.go:149
|
||||
#: pkg/contacts.go:143
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nombre y apellidos"
|
||||
|
||||
#: pkg/contacts.go:158
|
||||
#: pkg/contacts.go:152
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: pkg/contacts.go:164
|
||||
#: pkg/contacts.go:158
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nombre comercial"
|
||||
|
||||
#: pkg/contacts.go:169
|
||||
#: pkg/contacts.go:163
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Teléfono"
|
||||
|
||||
#: pkg/contacts.go:187
|
||||
#: pkg/contacts.go:181
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: pkg/contacts.go:195
|
||||
#: pkg/contacts.go:189
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Dirección"
|
||||
|
||||
#: pkg/contacts.go:204
|
||||
#: pkg/contacts.go:198
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Población"
|
||||
|
||||
#: pkg/contacts.go:210
|
||||
#: pkg/contacts.go:204
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Provincia"
|
||||
|
||||
#: pkg/contacts.go:216
|
||||
#: pkg/contacts.go:210
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Código postal"
|
||||
|
||||
#: pkg/contacts.go:225
|
||||
#: pkg/contacts.go:219
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: pkg/contacts.go:258
|
||||
#: pkg/contacts.go:252
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Habéis escogido un país que no es válido."
|
||||
|
||||
#: pkg/contacts.go:262
|
||||
#: pkg/contacts.go:256
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podéis dejar el nombre y los apellidos en blanco."
|
||||
|
||||
#: pkg/contacts.go:263
|
||||
#: pkg/contacts.go:257
|
||||
msgid "VAT number can not be empty."
|
||||
msgstr "No podéis dejar el DNI o NIF en blanco."
|
||||
|
||||
#: pkg/contacts.go:264
|
||||
#: pkg/contacts.go:258
|
||||
msgid "This value is not a valid VAT number."
|
||||
msgstr "Este valor no es un DNI o NIF válido."
|
||||
|
||||
#: pkg/contacts.go:266
|
||||
#: pkg/contacts.go:260
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podéis dejar el teléfono en blanco."
|
||||
|
||||
#: pkg/contacts.go:267
|
||||
#: pkg/contacts.go:261
|
||||
msgid "This value is not a valid phone number."
|
||||
msgstr "Este valor no es un teléfono válido."
|
||||
|
||||
#: pkg/contacts.go:273
|
||||
#: pkg/contacts.go:267
|
||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||
msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/."
|
||||
|
||||
#: pkg/contacts.go:275
|
||||
#: pkg/contacts.go:269
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podéis dejar la dirección en blanco."
|
||||
|
||||
#: pkg/contacts.go:276
|
||||
#: pkg/contacts.go:270
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podéis dejar la población en blanco."
|
||||
|
||||
#: pkg/contacts.go:277
|
||||
#: pkg/contacts.go:271
|
||||
msgid "Province can not be empty."
|
||||
msgstr "No podéis dejar la provincia en blanco."
|
||||
|
||||
#: pkg/contacts.go:278
|
||||
#: pkg/contacts.go:272
|
||||
msgid "Postal code can not be empty."
|
||||
msgstr "No podéis dejar el código postal en blanco."
|
||||
|
||||
#: pkg/contacts.go:279
|
||||
#: pkg/contacts.go:273
|
||||
msgid "This value is not a valid postal code."
|
||||
msgstr "Este valor no es un código postal válido válido."
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:invoice_amount from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop view if exists numerus.invoice_amount;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:invoice_tax_amount from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop view if exists numerus.invoice_tax_amount;
|
||||
|
||||
commit;
|
|
@ -56,3 +56,5 @@ new_invoice_product [schema_numerus] 2023-02-16T21:06:01Z jordi fita mas <jordi@
|
|||
invoice_number_counter [schema_numerus company] 2023-02-17T13:04:48Z jordi fita mas <jordi@tandem.blog> # Add relation to count invoice numbers
|
||||
next_invoice_number [schema_numerus invoice_number_counter] 2023-02-17T13:21:48Z jordi fita mas <jordi@tandem.blog> # Add function to retrieve the next invoice number
|
||||
add_invoice [schema_numerus invoice company currency parse_price new_invoice_product tax invoice_product invoice_product_tax next_invoice_number] 2023-02-16T21:12:46Z jordi fita mas <jordi@tandem.blog> # Add function to create new invoices
|
||||
invoice_tax_amount [schema_numerus invoice_product invoice_product_tax] 2023-02-22T12:08:35Z jordi fita mas <jordi@tandem.blog> # Add view for invoice tax amount
|
||||
invoice_amount [schema_numerus invoice_product invoice_tax_amount] 2023-02-22T12:58:46Z jordi fita mas <jordi@tandem.blog> # Add view to compute subtotal and total for invoices
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
-- Test invoice_amount
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_view('invoice_amount');
|
||||
select table_privs_are('invoice_amount', 'guest', array[]::text[]);
|
||||
select table_privs_are('invoice_amount', 'invoicer', array['SELECT']);
|
||||
select table_privs_are('invoice_amount', 'admin', array['SELECT']);
|
||||
select table_privs_are('invoice_amount', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('invoice_amount', 'invoice_id');
|
||||
select col_type_is('invoice_amount', 'invoice_id', 'integer');
|
||||
|
||||
select has_column('invoice_amount', 'subtotal');
|
||||
select col_type_is('invoice_amount', 'subtotal', 'integer');
|
||||
|
||||
select has_column('invoice_amount', 'total');
|
||||
select col_type_is('invoice_amount', 'total', 'integer');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact cascade;
|
||||
truncate product cascade;
|
||||
truncate tax cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code)
|
||||
values (1, 'Company 1', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR')
|
||||
;
|
||||
|
||||
insert into tax (tax_id, company_id, name, rate)
|
||||
values (2, 1, 'IRPF -15 %', -0.15)
|
||||
, (3, 1, 'IVA 4 %', 0.04)
|
||||
, (4, 1, 'IVA 10 %', 0.10)
|
||||
, (5, 1, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into product (product_id, company_id, name, price)
|
||||
values (6, 1, 'Product', 1212)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, invoice_date, contact_id, currency_code)
|
||||
values ( 8, 1, 'I1', current_date, 7, 'EUR')
|
||||
, ( 9, 1, 'I2', current_date, 7, 'EUR')
|
||||
, (10, 1, 'I3', current_date, 7, 'EUR')
|
||||
, (11, 1, 'I4', current_date, 7, 'EUR')
|
||||
;
|
||||
|
||||
insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price, quantity, discount_rate)
|
||||
values (12, 8, 6, 'P', 100, 1, 0.0)
|
||||
, (13, 8, 6, 'P', 200, 2, 0.1)
|
||||
, (14, 9, 6, 'P', 222, 3, 0.0)
|
||||
, (15, 9, 6, 'P', 333, 4, 0.2)
|
||||
, (16, 10, 6, 'P', 444, 5, 0.0)
|
||||
, (17, 10, 6, 'P', 555, 6, 0.1)
|
||||
, (18, 11, 6, 'P', 777, 8, 0.0)
|
||||
;
|
||||
|
||||
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
|
||||
values (12, 2, -0.15)
|
||||
, (12, 5, 0.21)
|
||||
, (13, 3, 0.04)
|
||||
, (14, 4, 0.10)
|
||||
, (14, 5, 0.21)
|
||||
, (14, 2, -0.07)
|
||||
, (15, 4, 0.10)
|
||||
, (16, 4, 0.10)
|
||||
, (16, 5, 0.21)
|
||||
, (17, 5, 0.21)
|
||||
, (17, 3, 0.04)
|
||||
;
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_id, subtotal, total from invoice_amount $$,
|
||||
$$ values ( 8, 460, 480)
|
||||
, ( 9, 1732, 1999)
|
||||
, (10, 5217, 6654)
|
||||
, (11, 6216, 6216)
|
||||
$$,
|
||||
'Should compute the amount for all taxes in the invoiced products.'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,107 @@
|
|||
-- Test invoice_tax_amount
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_view('invoice_tax_amount');
|
||||
select table_privs_are('invoice_tax_amount', 'guest', array[]::text[]);
|
||||
select table_privs_are('invoice_tax_amount', 'invoicer', array['SELECT']);
|
||||
select table_privs_are('invoice_tax_amount', 'admin', array['SELECT']);
|
||||
select table_privs_are('invoice_tax_amount', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('invoice_tax_amount', 'invoice_id');
|
||||
select col_type_is('invoice_tax_amount', 'invoice_id', 'integer');
|
||||
|
||||
select has_column('invoice_tax_amount', 'tax_id');
|
||||
select col_type_is('invoice_tax_amount', 'tax_id', 'integer');
|
||||
|
||||
select has_column('invoice_tax_amount', 'amount');
|
||||
select col_type_is('invoice_tax_amount', 'amount', 'integer');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact cascade;
|
||||
truncate product cascade;
|
||||
truncate tax cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code)
|
||||
values (1, 'Company 1', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR')
|
||||
;
|
||||
|
||||
insert into tax (tax_id, company_id, name, rate)
|
||||
values (2, 1, 'IRPF -15 %', -0.15)
|
||||
, (3, 1, 'IVA 4 %', 0.04)
|
||||
, (4, 1, 'IVA 10 %', 0.10)
|
||||
, (5, 1, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into product (product_id, company_id, name, price)
|
||||
values (6, 1, 'Product', 1212)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, invoice_date, contact_id, currency_code)
|
||||
values ( 8, 1, 'I1', current_date, 7, 'EUR')
|
||||
, ( 9, 1, 'I2', current_date, 7, 'EUR')
|
||||
, (10, 1, 'I3', current_date, 7, 'EUR')
|
||||
, (11, 1, 'I4', current_date, 7, 'EUR')
|
||||
;
|
||||
|
||||
insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price, quantity, discount_rate)
|
||||
values (12, 8, 6, 'P', 100, 1, 0.0)
|
||||
, (13, 8, 6, 'P', 200, 2, 0.1)
|
||||
, (14, 9, 6, 'P', 222, 3, 0.0)
|
||||
, (15, 9, 6, 'P', 333, 4, 0.2)
|
||||
, (16, 10, 6, 'P', 444, 5, 0.0)
|
||||
, (17, 10, 6, 'P', 555, 6, 0.1)
|
||||
, (18, 11, 6, 'P', 777, 8, 0.0)
|
||||
;
|
||||
|
||||
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
|
||||
values (12, 2, -0.15)
|
||||
, (12, 5, 0.21)
|
||||
, (13, 3, 0.04)
|
||||
, (14, 4, 0.10)
|
||||
, (14, 5, 0.21)
|
||||
, (14, 2, -0.07)
|
||||
, (15, 4, 0.10)
|
||||
, (16, 4, 0.10)
|
||||
, (16, 5, 0.21)
|
||||
, (17, 5, 0.21)
|
||||
, (17, 3, 0.04)
|
||||
;
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_id, tax_id, amount from invoice_tax_amount $$,
|
||||
$$ values ( 8, 2, -15)
|
||||
, ( 8, 3, 14)
|
||||
, ( 8, 5, 21)
|
||||
, ( 9, 2, -47)
|
||||
, ( 9, 4, 174)
|
||||
, ( 9, 5, 140)
|
||||
, (10, 3, 120)
|
||||
, (10, 4, 222)
|
||||
, (10, 5, 1095)
|
||||
$$,
|
||||
'Should compute the amount for all taxes in the invoiced products.'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,11 @@
|
|||
-- Verify numerus:invoice_amount on pg
|
||||
|
||||
begin;
|
||||
|
||||
select invoice_id
|
||||
, subtotal
|
||||
, total
|
||||
from numerus.invoice_amount
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,11 @@
|
|||
-- Verify numerus:invoice_tax_amount on pg
|
||||
|
||||
begin;
|
||||
|
||||
select invoice_id
|
||||
, tax_id
|
||||
, amount
|
||||
from numerus.invoice_tax_amount
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -504,6 +504,10 @@ main > nav {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.numeric {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Remix Icon */
|
||||
|
||||
@font-face {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<th>{{( pgettext "Customer" "title" )}}</th>
|
||||
<th>{{( pgettext "Status" "title" )}}</th>
|
||||
<th>{{( pgettext "Label" "title" )}}</th>
|
||||
<th>{{( pgettext "Amount" "title" )}}</th>
|
||||
<th>{{( pgettext "Download" "title" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -37,6 +38,7 @@
|
|||
<td><a href="{{ companyURI "/contacts/"}}{{ .CustomerSlug }}">{{ .CustomerName }}</a></td>
|
||||
<td class="invoice-status-{{ .Status }}">{{ .StatusLabel }}</td>
|
||||
<td></td>
|
||||
<td class="numeric">{{ .Total|formatPrice }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
|
|
Loading…
Reference in New Issue