Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
package pkg
import (
"context"
"errors"
2023-02-03 11:30:56 +00:00
"github.com/julienschmidt/httprouter"
2023-02-01 13:15:02 +00:00
"html/template"
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
"net/http"
"net/url"
2023-01-28 13:18:58 +00:00
"strconv"
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
)
const (
ContextCompanyKey = "numerus-company"
)
type Company struct {
Convert from cents to “price” and back
I do not want to use floats in the Go lang application, because it is
not supposed to do anything with these values other than to print and
retrieve them from the user; all computations will be performed by
PostgreSQL in cents.
That means i have to “convert” from the price format that users expect
to see (e.g., 1.234,56) to cents (e.g., 123456) and back when passing
data between Go and PostgreSQL, and that conversion depends on the
currency’s decimal places.
At first i did everything in Go, but saw that i would need to do it in
a loop when retrieving the list of products, and immediately knew it was
a mistake—i needed a PL/pgSQL function for that.
I still need to convert from string to float, however, when printing the
value to the user. Because the string representation is in C, but i
need to format it according to the locale with golang/x/text. That
package has the information of how to correctly format numbers, but it
is in an internal package that i can not use, and numbers.Digit only
accepts numeric types, not a string.
2023-02-05 12:55:12 +00:00
Id int
CurrencySymbol string
DecimalDigits int
Slug string
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
}
2023-02-03 11:30:56 +00:00
func CompanyHandler ( next http . Handler ) httprouter . Handle {
return func ( w http . ResponseWriter , r * http . Request , params httprouter . Params ) {
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
company := & Company {
2023-02-03 11:30:56 +00:00
Slug : params [ 0 ] . Value ,
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
}
2023-02-03 11:30:56 +00:00
conn := getConn ( r )
Convert from cents to “price” and back
I do not want to use floats in the Go lang application, because it is
not supposed to do anything with these values other than to print and
retrieve them from the user; all computations will be performed by
PostgreSQL in cents.
That means i have to “convert” from the price format that users expect
to see (e.g., 1.234,56) to cents (e.g., 123456) and back when passing
data between Go and PostgreSQL, and that conversion depends on the
currency’s decimal places.
At first i did everything in Go, but saw that i would need to do it in
a loop when retrieving the list of products, and immediately knew it was
a mistake—i needed a PL/pgSQL function for that.
I still need to convert from string to float, however, when printing the
value to the user. Because the string representation is in C, but i
need to format it according to the locale with golang/x/text. That
package has the information of how to correctly format numbers, but it
is in an internal package that i can not use, and numbers.Digit only
accepts numeric types, not a string.
2023-02-05 12:55:12 +00:00
err := conn . QueryRow ( r . Context ( ) , "select company_id, currency_symbol, decimal_digits from company join currency using (currency_code) where slug = $1" , company . Slug ) . Scan ( & company . Id , & company . CurrencySymbol , & company . DecimalDigits )
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
if err != nil {
http . NotFound ( w , r )
return
}
ctx := context . WithValue ( r . Context ( ) , ContextCompanyKey , company )
r = r . WithContext ( ctx )
2023-02-03 11:30:56 +00:00
r2 := new ( http . Request )
* r2 = * r
r2 . URL = new ( url . URL )
* r2 . URL = * r . URL
r2 . URL . Path = params [ 1 ] . Value
next . ServeHTTP ( w , r2 )
}
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
}
Convert from cents to “price” and back
I do not want to use floats in the Go lang application, because it is
not supposed to do anything with these values other than to print and
retrieve them from the user; all computations will be performed by
PostgreSQL in cents.
That means i have to “convert” from the price format that users expect
to see (e.g., 1.234,56) to cents (e.g., 123456) and back when passing
data between Go and PostgreSQL, and that conversion depends on the
currency’s decimal places.
At first i did everything in Go, but saw that i would need to do it in
a loop when retrieving the list of products, and immediately knew it was
a mistake—i needed a PL/pgSQL function for that.
I still need to convert from string to float, however, when printing the
value to the user. Because the string representation is in C, but i
need to format it according to the locale with golang/x/text. That
package has the information of how to correctly format numbers, but it
is in an internal package that i can not use, and numbers.Digit only
accepts numeric types, not a string.
2023-02-05 12:55:12 +00:00
func ( c Company ) MinCents ( ) float64 {
var r float64
r = 1
for i := 0 ; i < c . DecimalDigits ; i ++ {
r /= 10.0
}
return r
}
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
func getCompany ( r * http . Request ) * Company {
company := r . Context ( ) . Value ( ContextCompanyKey )
if company == nil {
return nil
}
return company . ( * Company )
}
2023-01-28 11:24:52 +00:00
type CurrencyOption struct {
Code string
Symbol string
}
2023-01-27 20:30:14 +00:00
type CountryOption struct {
Code string
Name string
}
2023-01-28 13:18:58 +00:00
type Tax struct {
2023-02-28 11:02:27 +00:00
Id int
Name string
Class string
Rate int
2023-01-28 13:18:58 +00:00
}
2023-03-03 15:49:06 +00:00
type PaymentMethod struct {
Id int
Name string
Instructions string
}
2023-02-01 13:15:02 +00:00
type taxDetailsForm struct {
2023-02-01 13:34:40 +00:00
* contactForm
2023-03-02 09:24:44 +00:00
Currency * SelectField
InvoiceNumberFormat * InputField
LegalDisclaimer * InputField
2023-02-01 13:15:02 +00:00
}
func newTaxDetailsForm ( ctx context . Context , conn * Conn , locale * Locale ) * taxDetailsForm {
return & taxDetailsForm {
2023-02-01 13:34:40 +00:00
contactForm : newContactForm ( ctx , conn , locale ) ,
2023-02-01 13:15:02 +00:00
Currency : & SelectField {
Name : "currency" ,
Label : pgettext ( "input" , "Currency" , locale ) ,
Options : MustGetOptions ( ctx , conn , "select currency_code, currency_symbol from currency order by currency_code" ) ,
2023-02-05 13:06:33 +00:00
Required : true ,
2023-02-08 12:47:36 +00:00
Selected : [ ] string { "EUR" } ,
2023-02-01 13:15:02 +00:00
} ,
2023-03-02 09:24:44 +00:00
InvoiceNumberFormat : & InputField {
Name : "invoice_number_format" ,
Label : pgettext ( "input" , "Invoice number format" , locale ) ,
Type : "text" ,
Required : true ,
} ,
LegalDisclaimer : & InputField {
Name : "legal_disclaimer" ,
Label : pgettext ( "input" , "Legal disclaimer" , locale ) ,
Type : "textarea" ,
} ,
2023-02-01 13:15:02 +00:00
}
}
func ( form * taxDetailsForm ) Parse ( r * http . Request ) error {
2023-02-01 13:34:40 +00:00
if err := form . contactForm . Parse ( r ) ; err != nil {
2023-02-01 13:15:02 +00:00
return err
}
form . Currency . FillValue ( r )
2023-03-02 09:24:44 +00:00
form . InvoiceNumberFormat . FillValue ( r )
form . LegalDisclaimer . FillValue ( r )
2023-02-01 13:15:02 +00:00
return nil
}
func ( form * taxDetailsForm ) Validate ( ctx context . Context , conn * Conn ) bool {
validator := newFormValidator ( )
validator . CheckValidSelectOption ( form . Currency , gettext ( "Selected currency is not valid." , form . locale ) )
2023-03-02 09:24:44 +00:00
validator . CheckRequiredInput ( form . InvoiceNumberFormat , gettext ( "Invoice number format can not be empty." , form . locale ) )
2023-02-01 13:34:40 +00:00
return form . contactForm . Validate ( ctx , conn ) && validator . AllOK ( )
2023-02-01 13:15:02 +00:00
}
func ( form * taxDetailsForm ) mustFillFromDatabase ( ctx context . Context , conn * Conn , company * Company ) * taxDetailsForm {
2023-03-02 09:24:44 +00:00
err := conn . QueryRow ( ctx , "select business_name, substr(vatin::text, 3), trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, invoice_number_format, legal_disclaimer from company where company_id = $1" , company . Id ) . Scan ( 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 )
2023-02-01 13:15:02 +00:00
if err != nil {
panic ( err )
}
return form
}
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
type TaxDetailsPage struct {
2023-03-03 15:49:06 +00:00
DetailsForm * taxDetailsForm
NewTaxForm * taxForm
Taxes [ ] * Tax
NewPaymentMethodForm * paymentMethodForm
PaymentMethods [ ] * PaymentMethod
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
}
2023-02-03 11:30:56 +00:00
func GetCompanyTaxDetailsForm ( w http . ResponseWriter , r * http . Request , _ httprouter . Params ) {
mustRenderTaxDetailsForm ( w , r , newTaxDetailsFormFromDatabase ( r ) )
}
func newTaxDetailsFormFromDatabase ( r * http . Request ) * taxDetailsForm {
locale := getLocale ( r )
conn := getConn ( r )
form := newTaxDetailsForm ( r . Context ( ) , conn , locale )
company := mustGetCompany ( r )
form . mustFillFromDatabase ( r . Context ( ) , conn , company )
return form
}
func HandleCompanyTaxDetailsForm ( w http . ResponseWriter , r * http . Request , _ httprouter . Params ) {
locale := getLocale ( r )
conn := getConn ( r )
form := newTaxDetailsForm ( r . Context ( ) , conn , locale )
if err := form . Parse ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusBadRequest )
return
}
if err := verifyCsrfTokenValid ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return
}
if ok := form . Validate ( r . Context ( ) , conn ) ; ! ok {
2023-03-21 10:58:54 +00:00
if ! IsHTMxRequest ( r ) {
w . WriteHeader ( http . StatusUnprocessableEntity )
}
2023-02-03 11:30:56 +00:00
mustRenderTaxDetailsForm ( w , r , form )
return
}
company := mustGetCompany ( r )
2023-03-02 09:24:44 +00:00
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 )
2023-03-21 10:58:54 +00:00
if IsHTMxRequest ( r ) {
w . Header ( ) . Set ( "HX-Trigger" , "closeModal" )
w . WriteHeader ( http . StatusNoContent )
} else {
http . Redirect ( w , r , companyURI ( company , "/tax-details" ) , http . StatusSeeOther )
}
2023-02-03 11:30:56 +00:00
}
func mustRenderTaxDetailsForm ( w http . ResponseWriter , r * http . Request , form * taxDetailsForm ) {
2023-03-03 15:49:06 +00:00
conn := getConn ( r )
locale := getLocale ( r )
2023-02-03 11:30:56 +00:00
page := & TaxDetailsPage {
2023-03-03 15:49:06 +00:00
DetailsForm : form ,
NewTaxForm : newTaxForm ( r . Context ( ) , conn , mustGetCompany ( r ) , locale ) ,
NewPaymentMethodForm : newPaymentMethodForm ( locale ) ,
2023-02-03 11:30:56 +00:00
}
mustRenderTexDetailsPage ( w , r , page )
}
func mustRenderTaxForm ( w http . ResponseWriter , r * http . Request , form * taxForm ) {
page := & TaxDetailsPage {
2023-03-03 15:49:06 +00:00
DetailsForm : newTaxDetailsFormFromDatabase ( r ) ,
NewTaxForm : form ,
NewPaymentMethodForm : newPaymentMethodForm ( getLocale ( r ) ) ,
}
mustRenderTexDetailsPage ( w , r , page )
}
func mustRenderPaymentMethodForm ( w http . ResponseWriter , r * http . Request , form * paymentMethodForm ) {
page := & TaxDetailsPage {
DetailsForm : newTaxDetailsFormFromDatabase ( r ) ,
NewTaxForm : newTaxForm ( r . Context ( ) , getConn ( r ) , mustGetCompany ( r ) , getLocale ( r ) ) ,
NewPaymentMethodForm : form ,
2023-02-03 11:30:56 +00:00
}
mustRenderTexDetailsPage ( w , r , page )
}
func mustRenderTexDetailsPage ( w http . ResponseWriter , r * http . Request , page * TaxDetailsPage ) {
conn := getConn ( r )
company := mustGetCompany ( r )
page . Taxes = mustGetTaxes ( r . Context ( ) , conn , company )
2023-03-03 15:49:06 +00:00
page . PaymentMethods = mustCollectPaymentMethods ( r . Context ( ) , conn , company )
2023-03-21 10:58:54 +00:00
mustRenderModalTemplate ( w , r , "tax-details.gohtml" , page )
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
}
func mustGetCompany ( r * http . Request ) * Company {
company := getCompany ( r )
if company == nil {
panic ( errors . New ( "company: required but not found" ) )
}
2023-01-27 00:08:03 +00:00
return company
Add the company relation and read-only form to edit
I do not have more time to update the update to the company today, but i
believe this is already a good amount of work for a commit.
The company is going to be used for row level security, as users will
only have access to the data from companies they are granted access, by
virtue of being in the company_user relation.
I did not know how add a row level security policy to the company_user
because i needed the to select on the same relation and this is not
allowed, because it would create an infinite loop.
Had to add the vat, pg_libphonenumber, and uri extensions in order to
validate VAT identification numbers, phone numbers, and URIs,
repectively. These libraries are not in Debian, but i created packages
for them all in https://dev.tandem.ws/tandem.
2023-01-24 20:46:07 +00:00
}
2023-01-27 20:30:14 +00:00
2023-02-01 13:15:02 +00:00
func mustGetTaxes ( ctx context . Context , conn * Conn , company * Company ) [ ] * Tax {
2023-02-28 11:02:27 +00:00
rows , err := conn . Query ( ctx , "select tax_id, tax.name, tax_class.name, (rate * 100)::integer from tax join tax_class using (tax_class_id) where tax.company_id = $1 order by rate, tax.name" , company . Id )
2023-01-27 20:30:14 +00:00
if err != nil {
panic ( err )
}
defer rows . Close ( )
2023-02-01 13:15:02 +00:00
var taxes [ ] * Tax
2023-01-27 20:30:14 +00:00
for rows . Next ( ) {
2023-02-01 13:15:02 +00:00
tax := & Tax { }
2023-02-28 11:02:27 +00:00
err = rows . Scan ( & tax . Id , & tax . Name , & tax . Class , & tax . Rate )
2023-01-27 20:30:14 +00:00
if err != nil {
panic ( err )
}
2023-02-01 13:15:02 +00:00
taxes = append ( taxes , tax )
2023-01-27 20:30:14 +00:00
}
if rows . Err ( ) != nil {
panic ( rows . Err ( ) )
}
2023-02-01 13:15:02 +00:00
return taxes
2023-01-27 20:30:14 +00:00
}
2023-01-28 11:24:52 +00:00
2023-03-03 15:49:06 +00:00
func mustCollectPaymentMethods ( ctx context . Context , conn * Conn , company * Company ) [ ] * PaymentMethod {
rows , err := conn . Query ( ctx , "select payment_method_id, name, instructions from payment_method where company_id = $1 order by name" , company . Id )
if err != nil {
panic ( err )
}
defer rows . Close ( )
var methods [ ] * PaymentMethod
for rows . Next ( ) {
method := & PaymentMethod { }
err = rows . Scan ( & method . Id , & method . Name , & method . Instructions )
if err != nil {
panic ( err )
}
methods = append ( methods , method )
}
if rows . Err ( ) != nil {
panic ( rows . Err ( ) )
}
return methods
}
2023-02-03 11:30:56 +00:00
type taxForm struct {
2023-02-01 13:15:02 +00:00
locale * Locale
Name * InputField
2023-02-28 11:02:27 +00:00
Class * SelectField
2023-02-01 13:15:02 +00:00
Rate * InputField
}
2023-01-28 11:24:52 +00:00
2023-02-28 11:02:27 +00:00
func newTaxForm ( ctx context . Context , conn * Conn , company * Company , locale * Locale ) * taxForm {
2023-02-03 11:30:56 +00:00
return & taxForm {
2023-02-01 13:15:02 +00:00
locale : locale ,
Name : & InputField {
Name : "tax_name" ,
Label : pgettext ( "input" , "Tax name" , locale ) ,
Type : "text" ,
Required : true ,
} ,
2023-02-28 11:02:27 +00:00
Class : & SelectField {
Name : "tax_class" ,
Label : pgettext ( "input" , "Tax Class" , locale ) ,
Options : MustGetOptions ( ctx , conn , "select tax_class_id::text, name from tax_class where company_id = $1 order by name" , company . Id ) ,
Required : true ,
EmptyLabel : gettext ( "Select a tax class" , locale ) ,
} ,
2023-02-01 13:15:02 +00:00
Rate : & InputField {
Name : "tax_rate" ,
Label : pgettext ( "input" , "Rate (%)" , locale ) ,
Type : "number" ,
Required : true ,
Attributes : [ ] template . HTMLAttr {
"min=-99" ,
"max=99" ,
} ,
} ,
2023-01-28 11:24:52 +00:00
}
}
2023-01-28 13:18:58 +00:00
2023-02-03 11:30:56 +00:00
func ( form * taxForm ) Parse ( r * http . Request ) error {
2023-02-01 13:15:02 +00:00
if err := r . ParseForm ( ) ; err != nil {
return err
2023-01-28 13:18:58 +00:00
}
2023-02-01 13:15:02 +00:00
form . Name . FillValue ( r )
2023-02-28 11:02:27 +00:00
form . Class . FillValue ( r )
2023-02-01 13:15:02 +00:00
form . Rate . FillValue ( r )
return nil
}
2023-01-28 13:18:58 +00:00
2023-02-03 11:30:56 +00:00
func ( form * taxForm ) Validate ( ) bool {
2023-02-01 13:15:02 +00:00
validator := newFormValidator ( )
validator . CheckRequiredInput ( form . Name , gettext ( "Tax name can not be empty." , form . locale ) )
2023-02-28 11:02:27 +00:00
validator . CheckValidSelectOption ( form . Class , gettext ( "Selected tax class is not valid." , form . locale ) )
2023-02-01 13:15:02 +00:00
if validator . CheckRequiredInput ( form . Rate , gettext ( "Tax rate can not be empty." , form . locale ) ) {
validator . CheckValidInteger ( form . Rate , - 99 , 99 , gettext ( "Tax rate must be an integer between -99 and 99." , form . locale ) )
2023-01-28 13:18:58 +00:00
}
2023-02-01 13:15:02 +00:00
return validator . AllOK ( )
2023-01-28 13:18:58 +00:00
}
2023-02-03 11:30:56 +00:00
func HandleDeleteCompanyTax ( w http . ResponseWriter , r * http . Request , params httprouter . Params ) {
taxId , err := strconv . Atoi ( params [ 0 ] . Value )
if err != nil {
http . NotFound ( w , r )
return
}
if err := verifyCsrfTokenValid ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return
}
conn := getConn ( r )
conn . MustExec ( r . Context ( ) , "delete from tax where tax_id = $1" , taxId )
2023-03-21 10:58:54 +00:00
if IsHTMxRequest ( r ) {
w . WriteHeader ( http . StatusOK )
} else {
http . Redirect ( w , r , companyURI ( mustGetCompany ( r ) , "/tax-details" ) , http . StatusSeeOther )
}
2023-02-03 11:30:56 +00:00
}
func HandleAddCompanyTax ( w http . ResponseWriter , r * http . Request , _ httprouter . Params ) {
locale := getLocale ( r )
2023-02-28 11:02:27 +00:00
conn := getConn ( r )
company := mustGetCompany ( r )
form := newTaxForm ( r . Context ( ) , conn , company , locale )
2023-02-03 11:30:56 +00:00
if err := form . Parse ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusBadRequest )
return
}
if err := verifyCsrfTokenValid ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return
}
if ! form . Validate ( ) {
2023-03-21 10:58:54 +00:00
if ! IsHTMxRequest ( r ) {
w . WriteHeader ( http . StatusUnprocessableEntity )
}
2023-02-03 11:30:56 +00:00
mustRenderTaxForm ( w , r , form )
return
}
2023-02-28 11:02:27 +00:00
conn . MustExec ( r . Context ( ) , "insert into tax (company_id, tax_class_id, name, rate) values ($1, $2, $3, $4 / 100::decimal)" , company . Id , form . Class , form . Name , form . Rate . Integer ( ) )
2023-03-21 10:58:54 +00:00
if IsHTMxRequest ( r ) {
mustRenderTaxForm ( w , r , newTaxForm ( r . Context ( ) , conn , company , locale ) )
} else {
http . Redirect ( w , r , companyURI ( company , "/tax-details" ) , http . StatusSeeOther )
}
2023-01-28 13:18:58 +00:00
}
2023-03-03 15:49:06 +00:00
type paymentMethodForm struct {
locale * Locale
Name * InputField
Instructions * InputField
}
func newPaymentMethodForm ( locale * Locale ) * paymentMethodForm {
return & paymentMethodForm {
locale : locale ,
Name : & InputField {
Name : "method_name" ,
Label : pgettext ( "input" , "Payment method name" , locale ) ,
Type : "text" ,
Required : true ,
} ,
Instructions : & InputField {
Name : "method_instructions" ,
Label : pgettext ( "input" , "Instructions" , locale ) ,
Type : "textarea" ,
Required : true ,
} ,
}
}
func ( form * paymentMethodForm ) Parse ( r * http . Request ) error {
if err := r . ParseForm ( ) ; err != nil {
return err
}
form . Name . FillValue ( r )
form . Instructions . FillValue ( r )
return nil
}
func ( form * paymentMethodForm ) Validate ( ) bool {
validator := newFormValidator ( )
validator . CheckRequiredInput ( form . Name , gettext ( "Payment method name can not be empty." , form . locale ) )
validator . CheckRequiredInput ( form . Instructions , gettext ( "Payment instructions can not be empty." , form . locale ) )
return validator . AllOK ( )
}
func HandleDeletePaymentMethod ( w http . ResponseWriter , r * http . Request , params httprouter . Params ) {
paymentMethodId , err := strconv . Atoi ( params [ 0 ] . Value )
if err != nil {
http . NotFound ( w , r )
return
}
if err := verifyCsrfTokenValid ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return
}
conn := getConn ( r )
conn . MustExec ( r . Context ( ) , "delete from payment_method where payment_method_id = $1" , paymentMethodId )
2023-03-21 10:58:54 +00:00
if IsHTMxRequest ( r ) {
w . WriteHeader ( http . StatusOK )
} else {
http . Redirect ( w , r , companyURI ( mustGetCompany ( r ) , "/tax-details" ) , http . StatusSeeOther )
}
2023-03-03 15:49:06 +00:00
}
func HandleAddPaymentMethod ( w http . ResponseWriter , r * http . Request , _ httprouter . Params ) {
locale := getLocale ( r )
conn := getConn ( r )
company := mustGetCompany ( r )
form := newPaymentMethodForm ( locale )
if err := form . Parse ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusBadRequest )
return
}
if err := verifyCsrfTokenValid ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return
}
if ! form . Validate ( ) {
2023-03-21 10:58:54 +00:00
if ! IsHTMxRequest ( r ) {
w . WriteHeader ( http . StatusUnprocessableEntity )
}
2023-03-03 15:49:06 +00:00
mustRenderPaymentMethodForm ( w , r , form )
return
}
conn . MustExec ( r . Context ( ) , "insert into payment_method (company_id, name, instructions) values ($1, $2, $3)" , company . Id , form . Name , form . Instructions )
2023-03-21 10:58:54 +00:00
if IsHTMxRequest ( r ) {
mustRenderPaymentMethodForm ( w , r , newPaymentMethodForm ( locale ) )
} else {
http . Redirect ( w , r , companyURI ( company , "/tax-details" ) , http . StatusSeeOther )
}
2023-03-03 15:49:06 +00:00
}