Compare commits

..

5 Commits

Author SHA1 Message Date
jordi fita mas 6c3a3ff232 Allow empty contact and payment method for quotes
I have to use a value to be used as “none” for payment method and
contact.  In PL/pgSQL add_quote and edit_quote functions, that value is
NULL, while in forms it is the empty string.  I can not simply pass the
empty string for either of these fields because PL/pgSQL expects
(nullable) integers, and "" is not a valid integer and is not NULL
either.  A conversion is necessary.

Apparently, Go’s nil is not a valid representation for SQL’s NULL with
pgx, and had to use sql.NullString instead.

I also needed to coalesce contact’s VATIN and phone, because null values
can not be scanned to *string.  I did not do that before because
`coalesce(vatin, '')` throws an error that '' is not a valid VATIN and
just left as is, wrongly expecting that pgx would do the job of leaving
the string blank for me.  It does not.

Lastly, i can not blindly write Quotee’s tax details in the quote’s view
page, or we would see the (), characters for the empty address info.
2023-06-08 13:05:41 +02:00
jordi fita mas 5537a53834 Add margin between quotee and terms and conditions 2023-06-08 12:55:12 +02:00
jordi fita mas 9bb5bcd820 Use the same style for quoter and quotee than invoicer and invoicee 2023-06-08 12:52:40 +02:00
jordi fita mas 9fab65f108 Add terms and conditions to invoice’s view 2023-06-08 12:52:10 +02:00
jordi fita mas ba6f51ac5d Fix getting quote’s terms and conditions from form and database 2023-06-08 12:50:16 +02:00
6 changed files with 144 additions and 100 deletions

View File

@ -2,6 +2,7 @@ package pkg
import ( import (
"context" "context"
"database/sql"
"database/sql/driver" "database/sql/driver"
"errors" "errors"
"fmt" "fmt"
@ -137,6 +138,13 @@ func (field *SelectField) String() string {
return field.Selected[0] return field.Selected[0]
} }
func (field *SelectField) OrNull() interface{} {
if field.String() == "" {
return sql.NullString{}
}
return field
}
func (field *SelectField) FillValue(r *http.Request) { func (field *SelectField) FillValue(r *http.Request) {
field.Selected = r.Form[field.Name] field.Selected = r.Form[field.Name]
} }

View File

@ -89,7 +89,7 @@ func mustCollectQuoteEntries(ctx context.Context, conn *Conn, company *Company,
select quote.slug select quote.slug
, quote_date , quote_date
, quote_number , quote_number
, contact.business_name , coalesce(contact.business_name, '')
, quote.tags , quote.tags
, quote.quote_status , quote.quote_status
, isi18n.name , isi18n.name
@ -310,6 +310,7 @@ type quote struct {
Slug string Slug string
Date time.Time Date time.Time
Quoter taxDetails Quoter taxDetails
HasQuotee bool
Quotee taxDetails Quotee taxDetails
TermsAndConditions string TermsAndConditions string
Notes string Notes string
@ -348,9 +349,10 @@ func mustGetQuote(ctx context.Context, conn *Conn, company *Company, slug string
, terms_and_conditions , terms_and_conditions
, notes , notes
, coalesce(instructions, '') , coalesce(instructions, '')
, contact_id is not null
, coalesce(business_name, '') , coalesce(business_name, '')
, vatin , coalesce(vatin::text, '')
, phone , coalesce(phone::text, '')
, coalesce(email, '') , coalesce(email, '')
, coalesce(address, '') , coalesce(address, '')
, coalesce(city, '') , coalesce(city, '')
@ -373,6 +375,7 @@ func mustGetQuote(ctx context.Context, conn *Conn, company *Company, slug string
&quo.TermsAndConditions, &quo.TermsAndConditions,
&quo.Notes, &quo.Notes,
&quo.PaymentInstructions, &quo.PaymentInstructions,
&quo.HasQuotee,
&quo.Quotee.Name, &quo.Quotee.Name,
&quo.Quotee.VATIN, &quo.Quotee.VATIN,
&quo.Quotee.Phone, &quo.Quotee.Phone,
@ -488,7 +491,7 @@ func HandleAddQuote(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
mustRenderNewQuoteForm(w, r, form) mustRenderNewQuoteForm(w, r, form)
return return
} }
slug := conn.MustGetText(r.Context(), "", "select add_quote($1, $2, $3, $4, $5, $6, $7, $8)", company.Id, form.Date, form.Customer, form.TermsAndConditions, form.Notes, form.PaymentMethod, form.Tags, NewQuoteProductArray(form.Products)) slug := conn.MustGetText(r.Context(), "", "select add_quote($1, $2, $3, $4, $5, $6, $7, $8)", company.Id, form.Date, form.Customer.OrNull(), form.TermsAndConditions, form.Notes, form.PaymentMethod.OrNull(), form.Tags, NewQuoteProductArray(form.Products))
htmxRedirect(w, r, companyURI(company, "/quotes/"+slug)) htmxRedirect(w, r, companyURI(company, "/quotes/"+slug))
} }
@ -620,6 +623,7 @@ func (form *quoteForm) Parse(r *http.Request) error {
form.QuoteStatus.FillValue(r) form.QuoteStatus.FillValue(r)
form.Customer.FillValue(r) form.Customer.FillValue(r)
form.Date.FillValue(r) form.Date.FillValue(r)
form.TermsAndConditions.FillValue(r)
form.Notes.FillValue(r) form.Notes.FillValue(r)
form.Tags.FillValue(r) form.Tags.FillValue(r)
form.PaymentMethod.FillValue(r) form.PaymentMethod.FillValue(r)
@ -745,6 +749,7 @@ func (form *quoteForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slu
, contact_id , contact_id
, quote_number , quote_number
, quote_date , quote_date
, terms_and_conditions
, notes , notes
, payment_method_id , payment_method_id
, tags , tags
@ -752,7 +757,7 @@ func (form *quoteForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slu
left join quote_contact using (quote_id) left join quote_contact using (quote_id)
left join quote_payment_method using (quote_id) left join quote_payment_method using (quote_id)
where slug = $1 where slug = $1
`, slug).Scan(&quoteId, form.QuoteStatus, form.Customer, &form.Number, form.Date, form.Notes, form.PaymentMethod, form.Tags)) { `, slug).Scan(&quoteId, form.QuoteStatus, form.Customer, &form.Number, form.Date, form.TermsAndConditions, form.Notes, form.PaymentMethod, form.Tags)) {
form.PaymentMethod.Selected = selectedPaymentMethod form.PaymentMethod.Selected = selectedPaymentMethod
form.QuoteStatus.Selected = selectedQuoteStatus form.QuoteStatus.Selected = selectedQuoteStatus
return false return false
@ -960,7 +965,7 @@ func HandleUpdateQuote(w http.ResponseWriter, r *http.Request, params httprouter
mustRenderEditQuoteForm(w, r, slug, form) mustRenderEditQuoteForm(w, r, slug, form)
return return
} }
slug = conn.MustGetText(r.Context(), "", "select edit_quote($1, $2, $3, $4, $5, $6, $7, $8)", slug, form.QuoteStatus, form.Customer, form.TermsAndConditions, form.Notes, form.PaymentMethod, form.Tags, EditedQuoteProductArray(form.Products)) slug = conn.MustGetText(r.Context(), "", "select edit_quote($1, $2, $3, $4, $5, $6, $7, $8)", slug, form.QuoteStatus, form.Customer.OrNull(), form.TermsAndConditions, form.Notes, form.PaymentMethod.OrNull(), form.Tags, EditedQuoteProductArray(form.Products))
if slug == "" { if slug == "" {
http.NotFound(w, r) http.NotFound(w, r)
return return

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-06-07 16:05+0200\n" "POT-Creation-Date: 2023-06-08 12:44+0200\n"
"PO-Revision-Date: 2023-01-18 17:08+0100\n" "PO-Revision-Date: 2023-01-18 17:08+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n" "Language-Team: Catalan <ca@dodds.net>\n"
@ -65,7 +65,7 @@ msgstr "Nom"
#: web/template/invoices/products.gohtml:50 #: web/template/invoices/products.gohtml:50
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50 #: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
#: web/template/quotes/view.gohtml:62 web/template/products/index.gohtml:42 #: web/template/quotes/view.gohtml:68 web/template/products/index.gohtml:42
msgctxt "title" msgctxt "title"
msgid "Price" msgid "Price"
msgstr "Preu" msgstr "Preu"
@ -95,15 +95,15 @@ msgstr "Desfes"
#: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67 #: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67
#: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61 #: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61
#: web/template/quotes/view.gohtml:67 web/template/quotes/edit.gohtml:62 #: web/template/quotes/view.gohtml:73 web/template/quotes/edit.gohtml:62
msgctxt "title" msgctxt "title"
msgid "Subtotal" msgid "Subtotal"
msgstr "Subtotal" msgstr "Subtotal"
#: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71 #: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71 #: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:71 #: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:77
#: web/template/quotes/view.gohtml:111 web/template/quotes/edit.gohtml:72 #: web/template/quotes/view.gohtml:117 web/template/quotes/edit.gohtml:72
msgctxt "title" msgctxt "title"
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
@ -226,22 +226,22 @@ msgctxt "action"
msgid "Download invoice" msgid "Download invoice"
msgstr "Descarrega factura" msgstr "Descarrega factura"
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:61 #: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:67
msgctxt "title" msgctxt "title"
msgid "Concept" msgid "Concept"
msgstr "Concepte" msgstr "Concepte"
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:64 #: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:70
msgctxt "title" msgctxt "title"
msgid "Discount" msgid "Discount"
msgstr "Descompte" msgstr "Descompte"
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:66 #: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:72
msgctxt "title" msgctxt "title"
msgid "Units" msgid "Units"
msgstr "Unitats" msgstr "Unitats"
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:101 #: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:107
msgctxt "title" msgctxt "title"
msgid "Tax Base" msgid "Tax Base"
msgstr "Base imposable" msgstr "Base imposable"
@ -353,6 +353,10 @@ msgctxt "action"
msgid "Download quotation" msgid "Download quotation"
msgstr "Descarrega pressupost" msgstr "Descarrega pressupost"
#: web/template/quotes/view.gohtml:60
msgid "Terms and Conditions:"
msgstr "Condicions dacceptació:"
#: web/template/quotes/edit.gohtml:2 web/template/quotes/edit.gohtml:19 #: web/template/quotes/edit.gohtml:2 web/template/quotes/edit.gohtml:19
msgctxt "title" msgctxt "title"
msgid "Edit Quotation “%s”" msgid "Edit Quotation “%s”"
@ -641,13 +645,13 @@ msgstr "No podeu deixar la contrasenya en blanc."
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nom dusuari o contrasenya incorrectes." msgstr "Nom dusuari o contrasenya incorrectes."
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:793 pkg/invoices.go:824 #: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:801 pkg/invoices.go:824
#: pkg/contacts.go:135 #: pkg/contacts.go:135
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:606 #: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:608
#: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189 #: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189
#: pkg/invoices.go:601 pkg/invoices.go:1123 pkg/contacts.go:140 #: pkg/invoices.go:601 pkg/invoices.go:1123 pkg/contacts.go:140
#: pkg/contacts.go:325 #: pkg/contacts.go:325
@ -683,39 +687,39 @@ msgstr "Qualsevol"
msgid "Invoices must have at least one of the specified labels." msgid "Invoices must have at least one of the specified labels."
msgstr "Les factures han de tenir com a mínim una de les etiquetes." msgstr "Les factures han de tenir com a mínim una de les etiquetes."
#: pkg/products.go:269 pkg/quote.go:807 pkg/invoices.go:838 #: pkg/products.go:269 pkg/quote.go:815 pkg/invoices.go:838
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripció" msgstr "Descripció"
#: pkg/products.go:274 pkg/quote.go:811 pkg/invoices.go:842 #: pkg/products.go:274 pkg/quote.go:819 pkg/invoices.go:842
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Preu" msgstr "Preu"
#: pkg/products.go:284 pkg/quote.go:840 pkg/expenses.go:181 pkg/invoices.go:871 #: pkg/products.go:284 pkg/quote.go:848 pkg/expenses.go:181 pkg/invoices.go:871
msgctxt "input" msgctxt "input"
msgid "Taxes" msgid "Taxes"
msgstr "Imposts" msgstr "Imposts"
#: pkg/products.go:309 pkg/quote.go:889 pkg/profile.go:92 pkg/invoices.go:920 #: pkg/products.go:309 pkg/quote.go:897 pkg/profile.go:92 pkg/invoices.go:920
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc." msgstr "No podeu deixar el nom en blanc."
#: pkg/products.go:310 pkg/quote.go:890 pkg/invoices.go:921 #: pkg/products.go:310 pkg/quote.go:898 pkg/invoices.go:921
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc." msgstr "No podeu deixar el preu en blanc."
#: pkg/products.go:311 pkg/quote.go:891 pkg/invoices.go:922 #: pkg/products.go:311 pkg/quote.go:899 pkg/invoices.go:922
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El preu ha de ser un número major a zero." msgstr "El preu ha de ser un número major a zero."
#: pkg/products.go:313 pkg/quote.go:899 pkg/expenses.go:227 pkg/expenses.go:232 #: pkg/products.go:313 pkg/quote.go:907 pkg/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:930 #: pkg/invoices.go:930
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid." msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/products.go:314 pkg/quote.go:900 pkg/expenses.go:228 pkg/expenses.go:233 #: pkg/products.go:314 pkg/quote.go:908 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:931 #: pkg/invoices.go:931
msgid "You can only select a tax of each class." msgid "You can only select a tax of each class."
msgstr "Només podeu seleccionar un impost de cada classe." msgstr "Només podeu seleccionar un impost de cada classe."
@ -805,7 +809,7 @@ msgstr "No podeu deixar el nom del mètode de pagament en blanc."
msgid "Payment instructions can not be empty." msgid "Payment instructions can not be empty."
msgstr "No podeu deixar les instruccions de pagament en blanc." msgstr "No podeu deixar les instruccions de pagament en blanc."
#: pkg/quote.go:161 pkg/quote.go:585 pkg/expenses.go:340 pkg/invoices.go:162 #: pkg/quote.go:161 pkg/quote.go:586 pkg/expenses.go:340 pkg/invoices.go:162
#: pkg/invoices.go:584 #: pkg/invoices.go:584
msgctxt "input" msgctxt "input"
msgid "Customer" msgid "Customer"
@ -815,7 +819,7 @@ msgstr "Client"
msgid "All customers" msgid "All customers"
msgstr "Tots els clients" msgstr "Tots els clients"
#: pkg/quote.go:167 pkg/quote.go:579 #: pkg/quote.go:167 pkg/quote.go:580
msgctxt "input" msgctxt "input"
msgid "Quotation Status" msgid "Quotation Status"
msgstr "Estat del pressupost" msgstr "Estat del pressupost"
@ -847,95 +851,99 @@ msgstr "Els pressuposts han de tenir totes les etiquetes."
msgid "Quotations must have at least one of the specified labels." msgid "Quotations must have at least one of the specified labels."
msgstr "Els pressuposts han de tenir com a mínim una de les etiquetes." msgstr "Els pressuposts han de tenir com a mínim una de les etiquetes."
#: pkg/quote.go:451 #: pkg/quote.go:528
msgid "Select a customer to quote."
msgstr "Escolliu un client a pressupostar."
#: pkg/quote.go:527
msgid "quotations.zip" msgid "quotations.zip"
msgstr "pressuposts.zip" msgstr "pressuposts.zip"
#: pkg/quote.go:533 pkg/quote.go:1055 pkg/quote.go:1063 pkg/invoices.go:533 #: pkg/quote.go:534 pkg/quote.go:1063 pkg/quote.go:1071 pkg/invoices.go:533
#: pkg/invoices.go:1098 pkg/invoices.go:1106 #: pkg/invoices.go:1098 pkg/invoices.go:1106
msgid "Invalid action" msgid "Invalid action"
msgstr "Acció invàlida." msgstr "Acció invàlida."
#: pkg/quote.go:590 #: pkg/quote.go:587
msgid "Select a customer to quote."
msgstr "Escolliu un client a pressupostar."
#: pkg/quote.go:592
msgctxt "input" msgctxt "input"
msgid "Quotation Date" msgid "Quotation Date"
msgstr "Data del pressupost" msgstr "Data del pressupost"
#: pkg/quote.go:596 #: pkg/quote.go:598
msgctxt "input" msgctxt "input"
msgid "Terms and conditions" msgid "Terms and conditions"
msgstr "Condicions dacceptació" msgstr "Condicions dacceptació"
#: pkg/quote.go:601 pkg/invoices.go:596 #: pkg/quote.go:603 pkg/invoices.go:596
msgctxt "input" msgctxt "input"
msgid "Notes" msgid "Notes"
msgstr "Notes" msgstr "Notes"
#: pkg/quote.go:610 pkg/invoices.go:606 #: pkg/quote.go:612 pkg/invoices.go:606
msgctxt "input" msgctxt "input"
msgid "Payment Method" msgid "Payment Method"
msgstr "Mètode de pagament" msgstr "Mètode de pagament"
#: pkg/quote.go:646 #: pkg/quote.go:613
msgid "Select a payment method."
msgstr "Escolliu un mètode de pagament."
#: pkg/quote.go:649
msgid "Selected quotation status is not valid." msgid "Selected quotation status is not valid."
msgstr "Heu seleccionat un estat de pressupost que no és vàlid." msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
#: pkg/quote.go:647 pkg/invoices.go:643 #: pkg/quote.go:651 pkg/invoices.go:643
msgid "Selected customer is not valid." msgid "Selected customer is not valid."
msgstr "Heu seleccionat un client que no és vàlid." msgstr "Heu seleccionat un client que no és vàlid."
#: pkg/quote.go:648 #: pkg/quote.go:653
msgid "Quotation date can not be empty." msgid "Quotation date can not be empty."
msgstr "No podeu deixar la data del pressupost en blanc." msgstr "No podeu deixar la data del pressupost en blanc."
#: pkg/quote.go:649 #: pkg/quote.go:654
msgid "Quotation date must be a valid date." msgid "Quotation date must be a valid date."
msgstr "La data del pressupost ha de ser vàlida." msgstr "La data del pressupost ha de ser vàlida."
#: pkg/quote.go:651 pkg/invoices.go:647 #: pkg/quote.go:657 pkg/invoices.go:647
msgid "Selected payment method is not valid." msgid "Selected payment method is not valid."
msgstr "Heu seleccionat un mètode de pagament que no és vàlid." msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
#: pkg/quote.go:783 pkg/quote.go:788 pkg/invoices.go:814 pkg/invoices.go:819 #: pkg/quote.go:791 pkg/quote.go:796 pkg/invoices.go:814 pkg/invoices.go:819
msgctxt "input" msgctxt "input"
msgid "Id" msgid "Id"
msgstr "Identificador" msgstr "Identificador"
#: pkg/quote.go:821 pkg/invoices.go:852 #: pkg/quote.go:829 pkg/invoices.go:852
msgctxt "input" msgctxt "input"
msgid "Quantity" msgid "Quantity"
msgstr "Quantitat" msgstr "Quantitat"
#: pkg/quote.go:830 pkg/invoices.go:861 #: pkg/quote.go:838 pkg/invoices.go:861
msgctxt "input" msgctxt "input"
msgid "Discount (%)" msgid "Discount (%)"
msgstr "Descompte (%)" msgstr "Descompte (%)"
#: pkg/quote.go:884 #: pkg/quote.go:892
msgid "Quotation product ID must be a number greater than zero." msgid "Quotation product ID must be a number greater than zero."
msgstr "LID del producte de pressupost ha de ser un número major a zero." msgstr "LID del producte de pressupost ha de ser un número major a zero."
#: pkg/quote.go:887 pkg/invoices.go:918 #: pkg/quote.go:895 pkg/invoices.go:918
msgid "Product ID must be a positive number or zero." msgid "Product ID must be a positive number or zero."
msgstr "LID del producte ha de ser un número positiu o zero." msgstr "LID del producte ha de ser un número positiu o zero."
#: pkg/quote.go:893 pkg/invoices.go:924 #: pkg/quote.go:901 pkg/invoices.go:924
msgid "Quantity can not be empty." msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc." msgstr "No podeu deixar la quantitat en blanc."
#: pkg/quote.go:894 pkg/invoices.go:925 #: pkg/quote.go:902 pkg/invoices.go:925
msgid "Quantity must be a number greater than zero." msgid "Quantity must be a number greater than zero."
msgstr "La quantitat ha de ser un número major a zero." msgstr "La quantitat ha de ser un número major a zero."
#: pkg/quote.go:896 pkg/invoices.go:927 #: pkg/quote.go:904 pkg/invoices.go:927
msgid "Discount can not be empty." msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc." msgstr "No podeu deixar el descompte en blanc."
#: pkg/quote.go:897 pkg/invoices.go:928 #: pkg/quote.go:905 pkg/invoices.go:928
msgid "Discount must be a percentage between 0 and 100." msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100." msgstr "El descompte ha de ser un percentatge entre 0 i 100."

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-06-07 16:05+0200\n" "POT-Creation-Date: 2023-06-08 12:44+0200\n"
"PO-Revision-Date: 2023-01-18 17:45+0100\n" "PO-Revision-Date: 2023-01-18 17:45+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n" "Language-Team: Spanish <es@tp.org.es>\n"
@ -65,7 +65,7 @@ msgstr "Nombre"
#: web/template/invoices/products.gohtml:50 #: web/template/invoices/products.gohtml:50
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50 #: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
#: web/template/quotes/view.gohtml:62 web/template/products/index.gohtml:42 #: web/template/quotes/view.gohtml:68 web/template/products/index.gohtml:42
msgctxt "title" msgctxt "title"
msgid "Price" msgid "Price"
msgstr "Precio" msgstr "Precio"
@ -95,15 +95,15 @@ msgstr "Deshacer"
#: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67 #: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67
#: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61 #: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61
#: web/template/quotes/view.gohtml:67 web/template/quotes/edit.gohtml:62 #: web/template/quotes/view.gohtml:73 web/template/quotes/edit.gohtml:62
msgctxt "title" msgctxt "title"
msgid "Subtotal" msgid "Subtotal"
msgstr "Subtotal" msgstr "Subtotal"
#: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71 #: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71 #: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:71 #: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:77
#: web/template/quotes/view.gohtml:111 web/template/quotes/edit.gohtml:72 #: web/template/quotes/view.gohtml:117 web/template/quotes/edit.gohtml:72
msgctxt "title" msgctxt "title"
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
@ -226,22 +226,22 @@ msgctxt "action"
msgid "Download invoice" msgid "Download invoice"
msgstr "Descargar factura" msgstr "Descargar factura"
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:61 #: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:67
msgctxt "title" msgctxt "title"
msgid "Concept" msgid "Concept"
msgstr "Concepto" msgstr "Concepto"
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:64 #: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:70
msgctxt "title" msgctxt "title"
msgid "Discount" msgid "Discount"
msgstr "Descuento" msgstr "Descuento"
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:66 #: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:72
msgctxt "title" msgctxt "title"
msgid "Units" msgid "Units"
msgstr "Unidades" msgstr "Unidades"
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:101 #: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:107
msgctxt "title" msgctxt "title"
msgid "Tax Base" msgid "Tax Base"
msgstr "Base imponible" msgstr "Base imponible"
@ -353,6 +353,10 @@ msgctxt "action"
msgid "Download quotation" msgid "Download quotation"
msgstr "Descargar presupuesto" msgstr "Descargar presupuesto"
#: web/template/quotes/view.gohtml:60
msgid "Terms and Conditions:"
msgstr "Condiciones de aceptación:"
#: web/template/quotes/edit.gohtml:2 web/template/quotes/edit.gohtml:19 #: web/template/quotes/edit.gohtml:2 web/template/quotes/edit.gohtml:19
msgctxt "title" msgctxt "title"
msgid "Edit Quotation “%s”" msgid "Edit Quotation “%s”"
@ -641,13 +645,13 @@ msgstr "No podéis dejar la contraseña en blanco."
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nombre de usuario o contraseña inválido." msgstr "Nombre de usuario o contraseña inválido."
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:793 pkg/invoices.go:824 #: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:801 pkg/invoices.go:824
#: pkg/contacts.go:135 #: pkg/contacts.go:135
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:606 #: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:608
#: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189 #: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189
#: pkg/invoices.go:601 pkg/invoices.go:1123 pkg/contacts.go:140 #: pkg/invoices.go:601 pkg/invoices.go:1123 pkg/contacts.go:140
#: pkg/contacts.go:325 #: pkg/contacts.go:325
@ -683,39 +687,39 @@ msgstr "Cualquiera"
msgid "Invoices must have at least one of the specified labels." msgid "Invoices must have at least one of the specified labels."
msgstr "Las facturas deben tener como mínimo una de las etiquetas." msgstr "Las facturas deben tener como mínimo una de las etiquetas."
#: pkg/products.go:269 pkg/quote.go:807 pkg/invoices.go:838 #: pkg/products.go:269 pkg/quote.go:815 pkg/invoices.go:838
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripción" msgstr "Descripción"
#: pkg/products.go:274 pkg/quote.go:811 pkg/invoices.go:842 #: pkg/products.go:274 pkg/quote.go:819 pkg/invoices.go:842
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Precio" msgstr "Precio"
#: pkg/products.go:284 pkg/quote.go:840 pkg/expenses.go:181 pkg/invoices.go:871 #: pkg/products.go:284 pkg/quote.go:848 pkg/expenses.go:181 pkg/invoices.go:871
msgctxt "input" msgctxt "input"
msgid "Taxes" msgid "Taxes"
msgstr "Impuestos" msgstr "Impuestos"
#: pkg/products.go:309 pkg/quote.go:889 pkg/profile.go:92 pkg/invoices.go:920 #: pkg/products.go:309 pkg/quote.go:897 pkg/profile.go:92 pkg/invoices.go:920
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco." msgstr "No podéis dejar el nombre en blanco."
#: pkg/products.go:310 pkg/quote.go:890 pkg/invoices.go:921 #: pkg/products.go:310 pkg/quote.go:898 pkg/invoices.go:921
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco." msgstr "No podéis dejar el precio en blanco."
#: pkg/products.go:311 pkg/quote.go:891 pkg/invoices.go:922 #: pkg/products.go:311 pkg/quote.go:899 pkg/invoices.go:922
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El precio tiene que ser un número mayor a cero." msgstr "El precio tiene que ser un número mayor a cero."
#: pkg/products.go:313 pkg/quote.go:899 pkg/expenses.go:227 pkg/expenses.go:232 #: pkg/products.go:313 pkg/quote.go:907 pkg/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:930 #: pkg/invoices.go:930
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido." msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/products.go:314 pkg/quote.go:900 pkg/expenses.go:228 pkg/expenses.go:233 #: pkg/products.go:314 pkg/quote.go:908 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:931 #: pkg/invoices.go:931
msgid "You can only select a tax of each class." msgid "You can only select a tax of each class."
msgstr "Solo podéis escoger un impuesto de cada clase." msgstr "Solo podéis escoger un impuesto de cada clase."
@ -805,7 +809,7 @@ msgstr "No podéis dejar el nombre del método de pago en blanco."
msgid "Payment instructions can not be empty." msgid "Payment instructions can not be empty."
msgstr "No podéis dejar las instrucciones de pago en blanco." msgstr "No podéis dejar las instrucciones de pago en blanco."
#: pkg/quote.go:161 pkg/quote.go:585 pkg/expenses.go:340 pkg/invoices.go:162 #: pkg/quote.go:161 pkg/quote.go:586 pkg/expenses.go:340 pkg/invoices.go:162
#: pkg/invoices.go:584 #: pkg/invoices.go:584
msgctxt "input" msgctxt "input"
msgid "Customer" msgid "Customer"
@ -815,7 +819,7 @@ msgstr "Cliente"
msgid "All customers" msgid "All customers"
msgstr "Todos los clientes" msgstr "Todos los clientes"
#: pkg/quote.go:167 pkg/quote.go:579 #: pkg/quote.go:167 pkg/quote.go:580
msgctxt "input" msgctxt "input"
msgid "Quotation Status" msgid "Quotation Status"
msgstr "Estado del presupuesto" msgstr "Estado del presupuesto"
@ -847,95 +851,99 @@ msgstr "Los presupuestos deben tener todas las etiquetas."
msgid "Quotations must have at least one of the specified labels." msgid "Quotations must have at least one of the specified labels."
msgstr "Los presupuestos deben tener como mínimo una de las etiquetas." msgstr "Los presupuestos deben tener como mínimo una de las etiquetas."
#: pkg/quote.go:451 #: pkg/quote.go:528
msgid "Select a customer to quote."
msgstr "Escoged un cliente a presupuestar."
#: pkg/quote.go:527
msgid "quotations.zip" msgid "quotations.zip"
msgstr "presupuestos.zip" msgstr "presupuestos.zip"
#: pkg/quote.go:533 pkg/quote.go:1055 pkg/quote.go:1063 pkg/invoices.go:533 #: pkg/quote.go:534 pkg/quote.go:1063 pkg/quote.go:1071 pkg/invoices.go:533
#: pkg/invoices.go:1098 pkg/invoices.go:1106 #: pkg/invoices.go:1098 pkg/invoices.go:1106
msgid "Invalid action" msgid "Invalid action"
msgstr "Acción inválida." msgstr "Acción inválida."
#: pkg/quote.go:590 #: pkg/quote.go:587
msgid "Select a customer to quote."
msgstr "Escoged un cliente a presupuestar."
#: pkg/quote.go:592
msgctxt "input" msgctxt "input"
msgid "Quotation Date" msgid "Quotation Date"
msgstr "Fecha del presupuesto" msgstr "Fecha del presupuesto"
#: pkg/quote.go:596 #: pkg/quote.go:598
msgctxt "input" msgctxt "input"
msgid "Terms and conditions" msgid "Terms and conditions"
msgstr "Condiciones de aceptación" msgstr "Condiciones de aceptación"
#: pkg/quote.go:601 pkg/invoices.go:596 #: pkg/quote.go:603 pkg/invoices.go:596
msgctxt "input" msgctxt "input"
msgid "Notes" msgid "Notes"
msgstr "Notas" msgstr "Notas"
#: pkg/quote.go:610 pkg/invoices.go:606 #: pkg/quote.go:612 pkg/invoices.go:606
msgctxt "input" msgctxt "input"
msgid "Payment Method" msgid "Payment Method"
msgstr "Método de pago" msgstr "Método de pago"
#: pkg/quote.go:646 #: pkg/quote.go:613
msgid "Select a payment method."
msgstr "Escoged un método e pago."
#: pkg/quote.go:649
msgid "Selected quotation status is not valid." msgid "Selected quotation status is not valid."
msgstr "Habéis escogido un estado de presupuesto que no es válido." msgstr "Habéis escogido un estado de presupuesto que no es válido."
#: pkg/quote.go:647 pkg/invoices.go:643 #: pkg/quote.go:651 pkg/invoices.go:643
msgid "Selected customer is not valid." msgid "Selected customer is not valid."
msgstr "Habéis escogido un cliente que no es válido." msgstr "Habéis escogido un cliente que no es válido."
#: pkg/quote.go:648 #: pkg/quote.go:653
msgid "Quotation date can not be empty." msgid "Quotation date can not be empty."
msgstr "No podéis dejar la fecha del presupuesto en blanco." msgstr "No podéis dejar la fecha del presupuesto en blanco."
#: pkg/quote.go:649 #: pkg/quote.go:654
msgid "Quotation date must be a valid date." msgid "Quotation date must be a valid date."
msgstr "La fecha de presupuesto debe ser válida." msgstr "La fecha de presupuesto debe ser válida."
#: pkg/quote.go:651 pkg/invoices.go:647 #: pkg/quote.go:657 pkg/invoices.go:647
msgid "Selected payment method is not valid." msgid "Selected payment method is not valid."
msgstr "Habéis escogido un método de pago que no es válido." msgstr "Habéis escogido un método de pago que no es válido."
#: pkg/quote.go:783 pkg/quote.go:788 pkg/invoices.go:814 pkg/invoices.go:819 #: pkg/quote.go:791 pkg/quote.go:796 pkg/invoices.go:814 pkg/invoices.go:819
msgctxt "input" msgctxt "input"
msgid "Id" msgid "Id"
msgstr "Identificador" msgstr "Identificador"
#: pkg/quote.go:821 pkg/invoices.go:852 #: pkg/quote.go:829 pkg/invoices.go:852
msgctxt "input" msgctxt "input"
msgid "Quantity" msgid "Quantity"
msgstr "Cantidad" msgstr "Cantidad"
#: pkg/quote.go:830 pkg/invoices.go:861 #: pkg/quote.go:838 pkg/invoices.go:861
msgctxt "input" msgctxt "input"
msgid "Discount (%)" msgid "Discount (%)"
msgstr "Descuento (%)" msgstr "Descuento (%)"
#: pkg/quote.go:884 #: pkg/quote.go:892
msgid "Quotation product ID must be a number greater than zero." msgid "Quotation product ID must be a number greater than zero."
msgstr "El ID de producto de presupuesto tiene que ser un número mayor a cero." msgstr "El ID de producto de presupuesto tiene que ser un número mayor a cero."
#: pkg/quote.go:887 pkg/invoices.go:918 #: pkg/quote.go:895 pkg/invoices.go:918
msgid "Product ID must be a positive number or zero." msgid "Product ID must be a positive number or zero."
msgstr "El ID de producto tiene que ser un número positivo o cero." msgstr "El ID de producto tiene que ser un número positivo o cero."
#: pkg/quote.go:893 pkg/invoices.go:924 #: pkg/quote.go:901 pkg/invoices.go:924
msgid "Quantity can not be empty." msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco." msgstr "No podéis dejar la cantidad en blanco."
#: pkg/quote.go:894 pkg/invoices.go:925 #: pkg/quote.go:902 pkg/invoices.go:925
msgid "Quantity must be a number greater than zero." msgid "Quantity must be a number greater than zero."
msgstr "La cantidad tiene que ser un número mayor a cero." msgstr "La cantidad tiene que ser un número mayor a cero."
#: pkg/quote.go:896 pkg/invoices.go:927 #: pkg/quote.go:904 pkg/invoices.go:927
msgid "Discount can not be empty." msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco." msgstr "No podéis dejar el descuento en blanco."
#: pkg/quote.go:897 pkg/invoices.go:928 #: pkg/quote.go:905 pkg/invoices.go:928
msgid "Discount must be a percentage between 0 and 100." msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100." msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."

View File

@ -20,7 +20,7 @@
padding: 0 0 0 2.5rem; padding: 0 0 0 2.5rem;
} }
.invoice .invoicee, .invoice .invoicer { .invoice .invoicee, .invoice .invoicer, .invoice .quotee, .invoice .quoter {
font-style: normal; font-style: normal;
} }
@ -28,11 +28,11 @@
margin-top: 2em; margin-top: 2em;
} }
.invoice .invoicer { .invoice .invoicer, .invoice .quoter {
margin-top: 9rem; margin-top: 9rem;
} }
.invoice .invoicee { .invoice .invoicee, .invoice .quotee {
margin-top: 1em; margin-top: 1em;
text-align: right; text-align: right;
} }
@ -55,11 +55,20 @@
text-align: right; text-align: right;
} }
.invoice .notes, .invoice .payment-instructions { .invoice .notes, .invoice .payment-instructions, .invoice .terms_and_conditions {
white-space: pre-line; white-space: pre-line;
text-align: left; text-align: left;
} }
.invoice .terms_and_conditions {
text-transform: uppercase;
font-weight: bold;
}
.invoice .quotee + .terms_and_conditions {
margin-top: 2rem;
}
.invoice .notes + .payment-instructions { .invoice .notes + .payment-instructions {
margin-top: 5rem; margin-top: 5rem;
} }

View File

@ -47,13 +47,19 @@
</header> </header>
<div> <div>
{{ if .HasQuotee -}}
<address class="quotee"> <address class="quotee">
{{ .Quotee.Name }}<br> {{ .Quotee.Name }}<br>
{{ .Quotee.VATIN }}<br> {{ .Quotee.VATIN }}<br>
{{ .Quotee.Address }}<br> {{ .Quotee.Address }}<br>
{{ .Quotee.City }} ({{ .Quotee.PostalCode}}), {{ .Quotee.Province }}<br> {{ .Quotee.City }} ({{ .Quotee.PostalCode}}), {{ .Quotee.Province }}<br>
</address> </address>
{{- end }}
{{ if .TermsAndConditions -}}
<p class="terms_and_conditions">{{(gettext "Terms and Conditions:")}} {{ .TermsAndConditions }}</p>
{{- end }}
{{- $columns := 5 | add (len .TaxClasses) | add (boolToInt .HasDiscounts) -}} {{- $columns := 5 | add (len .TaxClasses) | add (boolToInt .HasDiscounts) -}}
<table> <table>
<thead> <thead>