From f43949dd43bd2341bfca2f359592edfc1dc5530f Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Fri, 9 Jun 2023 12:43:50 +0200 Subject: [PATCH] Add quote number formatting and next number field to tax details The same as for invoices: to allow people to have their own numbering scheme, and for these that start using the program in the middle of the current year. --- pkg/company.go | 89 ++++++++++++++++++++++++++++++--- pkg/db.go | 6 +++ po/ca.po | 86 ++++++++++++++++++------------- po/es.po | 88 +++++++++++++++++++------------- web/template/tax-details.gohtml | 4 +- 5 files changed, 197 insertions(+), 76 deletions(-) diff --git a/pkg/company.go b/pkg/company.go index 8dcc4e0..35aca21 100644 --- a/pkg/company.go +++ b/pkg/company.go @@ -89,6 +89,8 @@ type taxDetailsForm struct { Currency *SelectField InvoiceNumberFormat *InputField NextInvoiceNumber *InputField + QuoteNumberFormat *InputField + NextQuoteNumber *InputField LegalDisclaimer *InputField } @@ -117,6 +119,21 @@ func newTaxDetailsForm(ctx context.Context, conn *Conn, locale *Locale) *taxDeta "min=1", }, }, + QuoteNumberFormat: &InputField{ + Name: "quote_number_format", + Label: pgettext("input", "Quotation number format", locale), + Type: "text", + Required: true, + }, + NextQuoteNumber: &InputField{ + Name: "next_quotation_number", + Label: pgettext("input", "Next quotation number", locale), + Type: "number", + Required: true, + Attributes: []template.HTMLAttr{ + "min=1", + }, + }, LegalDisclaimer: &InputField{ Name: "legal_disclaimer", Label: pgettext("input", "Legal disclaimer", locale), @@ -132,6 +149,8 @@ func (form *taxDetailsForm) Parse(r *http.Request) error { form.Currency.FillValue(r) form.InvoiceNumberFormat.FillValue(r) form.NextInvoiceNumber.FillValue(r) + form.QuoteNumberFormat.FillValue(r) + form.NextQuoteNumber.FillValue(r) form.LegalDisclaimer.FillValue(r) return nil } @@ -141,6 +160,8 @@ func (form *taxDetailsForm) Validate(ctx context.Context, conn *Conn) bool { validator.CheckValidSelectOption(form.Currency, gettext("Selected currency is not valid.", form.locale)) validator.CheckRequiredInput(form.InvoiceNumberFormat, gettext("Invoice number format can not be empty.", form.locale)) validator.CheckValidInteger(form.NextInvoiceNumber, 1, math.MaxInt32, gettext("Next invoice number must be a number greater than zero.", form.locale)) + validator.CheckRequiredInput(form.QuoteNumberFormat, gettext("Quotation number format can not be empty.", form.locale)) + validator.CheckValidInteger(form.NextQuoteNumber, 1, math.MaxInt32, gettext("Next quotation number must be a number greater than zero.", form.locale)) return form.contactForm.Validate(ctx, conn) && validator.AllOK() } @@ -159,12 +180,17 @@ func (form *taxDetailsForm) mustFillFromDatabase(ctx context.Context, conn *Conn , country_code , currency_code , invoice_number_format + , quote_number_format , legal_disclaimer , coalesce(invoice_number_counter.currval, 0) + 1 + , coalesce(quote_number_counter.currval, 0) + 1 from company left join invoice_number_counter on invoice_number_counter.company_id = company.company_id - and year = date_part('year', current_date) + and invoice_number_counter.year = date_part('year', current_date) + left join quote_number_counter + on quote_number_counter.company_id = company.company_id + and quote_number_counter.year = date_part('year', current_date) where company.company_id = $1`, company.Id).Scan( form.BusinessName, form.VATIN, @@ -179,15 +205,14 @@ func (form *taxDetailsForm) mustFillFromDatabase(ctx context.Context, conn *Conn form.Country, form.Currency, form.InvoiceNumberFormat, + form.QuoteNumberFormat, form.LegalDisclaimer, form.NextInvoiceNumber, + form.NextQuoteNumber, ) if err != nil { panic(err) } - if err != nil { - panic(err) - } return form } @@ -234,8 +259,60 @@ func HandleCompanyTaxDetailsForm(w http.ResponseWriter, r *http.Request, _ httpr return } company := mustGetCompany(r) - conn.MustExec(r.Context(), "update company set business_name = $1, vatin = ($11 || $2)::vatin, trade_name = $3, phone = parse_packed_phone_number($4, $11), email = $5, web = $6, address = $7, city = $8, province = $9, postal_code = $10, country_code = $11, currency_code = $12, invoice_number_format = $13, legal_disclaimer = $14 where company_id = $15", form.BusinessName, form.VATIN, form.TradeName, form.Phone, form.Email, form.Web, form.Address, form.City, form.Province, form.PostalCode, form.Country, form.Currency, form.InvoiceNumberFormat, form.LegalDisclaimer, company.Id) - conn.MustExec(r.Context(), "update invoice_number_counter set currval = $1 where company_id = $2 and year = date_part('year', current_date)", form.NextInvoiceNumber.Integer()-1, company.Id) + tx := conn.MustBegin(r.Context()) + defer tx.MustRollback(r.Context()) + tx.MustExec(r.Context(), ` + update company + set business_name = $1 + , vatin = ($11 || $2)::vatin + , trade_name = $3 + , phone = parse_packed_phone_number($4, $11) + , email = $5 + , web = $6 + , address = $7 + , city = $8 + , province = $9 + , postal_code = $10 + , country_code = $11 + , currency_code = $12 + , invoice_number_format = $13 + , quote_number_format = $14 + , legal_disclaimer = $15 + where company_id = $16 + `, + form.BusinessName, + form.VATIN, + form.TradeName, + form.Phone, + form.Email, + form.Web, + form.Address, + form.City, + form.Province, + form.PostalCode, + form.Country, + form.Currency, + form.InvoiceNumberFormat, + form.QuoteNumberFormat, + form.LegalDisclaimer, + company.Id) + tx.MustExec(r.Context(), ` + insert into invoice_number_counter (company_id, year, currval) + values ($1, date_part('year', current_date), $2) + on conflict (company_id, year) do update + set currval = excluded.currval + `, + company.Id, + form.NextInvoiceNumber.Integer()-1) + tx.MustExec(r.Context(), ` + insert into quote_number_counter (company_id, year, currval) + values ($1, date_part('year', current_date), $2) + on conflict (company_id, year) do update + set currval = excluded.currval + `, + company.Id, + form.NextQuoteNumber.Integer()-1) + tx.MustCommit(r.Context()) if IsHTMxRequest(r) { w.Header().Set(HxTrigger, "closeModal") w.WriteHeader(http.StatusNoContent) diff --git a/pkg/db.go b/pkg/db.go index dc3ea7c..4ba06e5 100644 --- a/pkg/db.go +++ b/pkg/db.go @@ -137,6 +137,12 @@ func (tx *Tx) MustRollback(ctx context.Context) { } } +func (tx *Tx) MustExec(ctx context.Context, sql string, args ...interface{}) { + if _, err := tx.Exec(ctx, sql, args...); err != nil { + panic(err) + } +} + func (tx *Tx) MustGetInteger(ctx context.Context, sql string, args ...interface{}) int { var result int if err := tx.QueryRow(ctx, sql, args...).Scan(&result); err != nil { diff --git a/po/ca.po b/po/ca.po index 71f1389..f497e96 100644 --- a/po/ca.po +++ b/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-06-08 12:44+0200\n" +"POT-Creation-Date: 2023-06-09 12:38+0200\n" "PO-Revision-Date: 2023-01-18 17:08+0100\n" "Last-Translator: jordi fita mas \n" "Language-Team: Catalan \n" @@ -474,7 +474,7 @@ msgctxt "title" msgid "Language" msgstr "Idioma" -#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:173 +#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:175 msgctxt "action" msgid "Save changes" msgstr "Desa canvis" @@ -538,57 +538,57 @@ msgstr "Moneda" #: web/template/tax-details.gohtml:41 msgctxt "title" -msgid "Invoicing" -msgstr "Facturació" +msgid "Invoicing and Quoting" +msgstr "Facturació i pressuposts" -#: web/template/tax-details.gohtml:54 +#: web/template/tax-details.gohtml:56 msgid "Are you sure?" msgstr "N’esteu segur?" -#: web/template/tax-details.gohtml:60 +#: web/template/tax-details.gohtml:62 msgctxt "title" msgid "Tax Name" msgstr "Nom impost" -#: web/template/tax-details.gohtml:61 +#: web/template/tax-details.gohtml:63 msgctxt "title" msgid "Rate (%)" msgstr "Percentatge" -#: web/template/tax-details.gohtml:62 +#: web/template/tax-details.gohtml:64 msgctxt "title" msgid "Class" msgstr "Classe" -#: web/template/tax-details.gohtml:86 +#: web/template/tax-details.gohtml:88 msgid "No taxes added yet." msgstr "No hi ha cap impost." -#: web/template/tax-details.gohtml:92 web/template/tax-details.gohtml:153 +#: web/template/tax-details.gohtml:94 web/template/tax-details.gohtml:155 msgctxt "title" msgid "New Line" msgstr "Nova línia" -#: web/template/tax-details.gohtml:106 +#: web/template/tax-details.gohtml:108 msgctxt "action" msgid "Add new tax" msgstr "Afegeix nou impost" -#: web/template/tax-details.gohtml:122 +#: web/template/tax-details.gohtml:124 msgctxt "title" msgid "Payment Method" msgstr "Mètode de pagament" -#: web/template/tax-details.gohtml:123 +#: web/template/tax-details.gohtml:125 msgctxt "title" msgid "Instructions" msgstr "Instruccions" -#: web/template/tax-details.gohtml:147 +#: web/template/tax-details.gohtml:149 msgid "No payment methods added yet." msgstr "No hi ha cap mètode de pagament." -#: web/template/tax-details.gohtml:165 +#: web/template/tax-details.gohtml:167 msgctxt "action" msgid "Add new payment method" msgstr "Afegeix nou mètode de pagament" @@ -724,88 +724,106 @@ msgstr "Heu seleccionat un impost que no és vàlid." msgid "You can only select a tax of each class." msgstr "Només podeu seleccionar un impost de cada classe." -#: pkg/company.go:100 +#: pkg/company.go:102 msgctxt "input" msgid "Currency" msgstr "Moneda" -#: pkg/company.go:107 +#: pkg/company.go:109 msgctxt "input" msgid "Invoice number format" msgstr "Format del número de factura" -#: pkg/company.go:113 +#: pkg/company.go:115 msgctxt "input" msgid "Next invoice number" msgstr "Següent número de factura" -#: pkg/company.go:122 +#: pkg/company.go:124 +msgctxt "input" +msgid "Quotation number format" +msgstr "Format del número de pressupost" + +#: pkg/company.go:130 +msgctxt "input" +msgid "Next quotation number" +msgstr "Següent número de pressupost" + +#: pkg/company.go:139 msgctxt "input" msgid "Legal disclaimer" msgstr "Nota legal" -#: pkg/company.go:141 +#: pkg/company.go:160 msgid "Selected currency is not valid." msgstr "Heu seleccionat una moneda que no és vàlida." -#: pkg/company.go:142 +#: pkg/company.go:161 msgid "Invoice number format can not be empty." msgstr "No podeu deixar el format del número de factura en blanc." -#: pkg/company.go:143 +#: pkg/company.go:162 msgid "Next invoice number must be a number greater than zero." msgstr "El següent número de factura ha de ser un número major a zero." -#: pkg/company.go:350 +#: pkg/company.go:163 +msgid "Quotation number format can not be empty." +msgstr "No podeu deixar el format del número de pressupost en blanc." + +#: pkg/company.go:164 +msgid "Next quotation number must be a number greater than zero." +msgstr "El següent número de pressupost ha de ser un número major a zero." + +#: pkg/company.go:427 msgctxt "input" msgid "Tax name" msgstr "Nom impost" -#: pkg/company.go:356 +#: pkg/company.go:433 msgctxt "input" msgid "Tax Class" msgstr "Classe d’impost" -#: pkg/company.go:359 +#: pkg/company.go:436 msgid "Select a tax class" msgstr "Escolliu una classe d’impost" -#: pkg/company.go:363 +#: pkg/company.go:440 msgctxt "input" msgid "Rate (%)" msgstr "Percentatge" -#: pkg/company.go:386 +#: pkg/company.go:463 msgid "Tax name can not be empty." msgstr "No podeu deixar el nom de l’impost en blanc." -#: pkg/company.go:387 +#: pkg/company.go:464 msgid "Selected tax class is not valid." msgstr "Heu seleccionat una classe d’impost que no és vàlida." -#: pkg/company.go:388 +#: pkg/company.go:465 msgid "Tax rate can not be empty." msgstr "No podeu deixar percentatge en blanc." -#: pkg/company.go:389 +#: pkg/company.go:466 msgid "Tax rate must be an integer between -99 and 99." msgstr "El percentatge ha de ser entre -99 i 99." -#: pkg/company.go:452 +#: pkg/company.go:529 msgctxt "input" msgid "Payment method name" msgstr "Nom del mètode de pagament" -#: pkg/company.go:458 +#: pkg/company.go:535 msgctxt "input" msgid "Instructions" msgstr "Instruccions" -#: pkg/company.go:476 +#: pkg/company.go:553 msgid "Payment method name can not be empty." msgstr "No podeu deixar el nom del mètode de pagament en blanc." -#: pkg/company.go:477 +#: pkg/company.go:554 msgid "Payment instructions can not be empty." msgstr "No podeu deixar les instruccions de pagament en blanc." diff --git a/po/es.po b/po/es.po index 51d5a5f..608bde8 100644 --- a/po/es.po +++ b/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-06-08 12:44+0200\n" +"POT-Creation-Date: 2023-06-09 12:38+0200\n" "PO-Revision-Date: 2023-01-18 17:45+0100\n" "Last-Translator: jordi fita mas \n" "Language-Team: Spanish \n" @@ -474,7 +474,7 @@ msgctxt "title" msgid "Language" msgstr "Idioma" -#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:173 +#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:175 msgctxt "action" msgid "Save changes" msgstr "Guardar cambios" @@ -538,57 +538,57 @@ msgstr "Moneda" #: web/template/tax-details.gohtml:41 msgctxt "title" -msgid "Invoicing" -msgstr "Facturación" +msgid "Invoicing and Quoting" +msgstr "Facturación y presupuestos" -#: web/template/tax-details.gohtml:54 +#: web/template/tax-details.gohtml:56 msgid "Are you sure?" msgstr "¿Estáis seguro?" -#: web/template/tax-details.gohtml:60 +#: web/template/tax-details.gohtml:62 msgctxt "title" msgid "Tax Name" msgstr "Nombre impuesto" -#: web/template/tax-details.gohtml:61 +#: web/template/tax-details.gohtml:63 msgctxt "title" msgid "Rate (%)" msgstr "Porcentaje" -#: web/template/tax-details.gohtml:62 +#: web/template/tax-details.gohtml:64 msgctxt "title" msgid "Class" msgstr "Clase" -#: web/template/tax-details.gohtml:86 +#: web/template/tax-details.gohtml:88 msgid "No taxes added yet." msgstr "No hay impuestos." -#: web/template/tax-details.gohtml:92 web/template/tax-details.gohtml:153 +#: web/template/tax-details.gohtml:94 web/template/tax-details.gohtml:155 msgctxt "title" msgid "New Line" msgstr "Nueva línea" -#: web/template/tax-details.gohtml:106 +#: web/template/tax-details.gohtml:108 msgctxt "action" msgid "Add new tax" msgstr "Añadir nuevo impuesto" -#: web/template/tax-details.gohtml:122 +#: web/template/tax-details.gohtml:124 msgctxt "title" msgid "Payment Method" msgstr "Método de pago" -#: web/template/tax-details.gohtml:123 +#: web/template/tax-details.gohtml:125 msgctxt "title" msgid "Instructions" msgstr "Instrucciones" -#: web/template/tax-details.gohtml:147 +#: web/template/tax-details.gohtml:149 msgid "No payment methods added yet." msgstr "No hay métodos de pago." -#: web/template/tax-details.gohtml:165 +#: web/template/tax-details.gohtml:167 msgctxt "action" msgid "Add new payment method" msgstr "Añadir nuevo método de pago" @@ -724,88 +724,106 @@ msgstr "Habéis escogido un impuesto que no es válido." msgid "You can only select a tax of each class." msgstr "Solo podéis escoger un impuesto de cada clase." -#: pkg/company.go:100 +#: pkg/company.go:102 msgctxt "input" msgid "Currency" msgstr "Moneda" -#: pkg/company.go:107 +#: pkg/company.go:109 msgctxt "input" msgid "Invoice number format" msgstr "Formato del número de factura" -#: pkg/company.go:113 +#: pkg/company.go:115 msgctxt "input" msgid "Next invoice number" -msgstr "Número de presupuesto" +msgstr "Siguiente número de factura" -#: pkg/company.go:122 +#: pkg/company.go:124 +msgctxt "input" +msgid "Quotation number format" +msgstr "Formato del número de presupuesto" + +#: pkg/company.go:130 +msgctxt "input" +msgid "Next quotation number" +msgstr "Siguiente número de presupuesto" + +#: pkg/company.go:139 msgctxt "input" msgid "Legal disclaimer" msgstr "Nota legal" -#: pkg/company.go:141 +#: pkg/company.go:160 msgid "Selected currency is not valid." msgstr "Habéis escogido una moneda que no es válida." -#: pkg/company.go:142 +#: pkg/company.go:161 msgid "Invoice number format can not be empty." msgstr "No podéis dejar el formato del número de factura en blanco." -#: pkg/company.go:143 +#: pkg/company.go:162 msgid "Next invoice number must be a number greater than zero." msgstr "El siguiente número de factura tiene que ser un número mayor a cero." -#: pkg/company.go:350 +#: pkg/company.go:163 +msgid "Quotation number format can not be empty." +msgstr "No podéis dejar el formato del número de presupuesto en blanco." + +#: pkg/company.go:164 +msgid "Next quotation number must be a number greater than zero." +msgstr "El siguiente número de presupuesto tiene que ser un número mayor a cero." + +#: pkg/company.go:427 msgctxt "input" msgid "Tax name" msgstr "Nombre impuesto" -#: pkg/company.go:356 +#: pkg/company.go:433 msgctxt "input" msgid "Tax Class" msgstr "Clase de impuesto" -#: pkg/company.go:359 +#: pkg/company.go:436 msgid "Select a tax class" msgstr "Escoged una clase de impuesto" -#: pkg/company.go:363 +#: pkg/company.go:440 msgctxt "input" msgid "Rate (%)" msgstr "Porcentaje" -#: pkg/company.go:386 +#: pkg/company.go:463 msgid "Tax name can not be empty." msgstr "No podéis dejar el nombre del impuesto en blanco." -#: pkg/company.go:387 +#: pkg/company.go:464 msgid "Selected tax class is not valid." msgstr "Habéis escogido una clase impuesto que no es válida." -#: pkg/company.go:388 +#: pkg/company.go:465 msgid "Tax rate can not be empty." msgstr "No podéis dejar el porcentaje en blanco." -#: pkg/company.go:389 +#: pkg/company.go:466 msgid "Tax rate must be an integer between -99 and 99." msgstr "El porcentaje tiene que estar entre -99 y 99." -#: pkg/company.go:452 +#: pkg/company.go:529 msgctxt "input" msgid "Payment method name" msgstr "Nombre del método de pago" -#: pkg/company.go:458 +#: pkg/company.go:535 msgctxt "input" msgid "Instructions" msgstr "Instrucciones" -#: pkg/company.go:476 +#: pkg/company.go:553 msgid "Payment method name can not be empty." msgstr "No podéis dejar el nombre del método de pago en blanco." -#: pkg/company.go:477 +#: pkg/company.go:554 msgid "Payment instructions can not be empty." msgstr "No podéis dejar las instrucciones de pago en blanco." diff --git a/web/template/tax-details.gohtml b/web/template/tax-details.gohtml index 4a56f2d..cf8cc7c 100644 --- a/web/template/tax-details.gohtml +++ b/web/template/tax-details.gohtml @@ -38,10 +38,12 @@
- {{( pgettext "Invoicing" "title" )}} + {{( pgettext "Invoicing and Quoting" "title" )}} {{ template "input-field" .InvoiceNumberFormat }} {{ template "input-field" .NextInvoiceNumber }} + {{ template "input-field" .QuoteNumberFormat }} + {{ template "input-field" .NextQuoteNumber }} {{ template "input-field" .LegalDisclaimer }}