2023-08-15 20:35:21 +00:00
/ *
* SPDX - FileCopyrightText : 2023 jordi fita mas < jfita @ peritasoft . com >
* SPDX - License - Identifier : AGPL - 3.0 - only
* /
package company
import (
"context"
"net/http"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/locale"
"dev.tandem.ws/tandem/camper/pkg/template"
)
type AdminHandler struct {
}
func NewAdminHandler ( ) * AdminHandler {
return & AdminHandler { }
}
func ( h * AdminHandler ) Handler ( user * auth . User , company * auth . Company , conn * database . Conn ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
var head string
head , r . URL . Path = httplib . ShiftPath ( r . URL . Path )
switch head {
case "" :
switch r . Method {
case http . MethodGet :
f := newTaxDetailsForm ( r . Context ( ) , conn , user . Locale )
if err := f . FillFromDatabase ( r . Context ( ) , company , conn ) ; err != nil {
panic ( err )
}
f . MustRender ( w , r , user , company )
case http . MethodPut :
editTaxDetails ( w , r , user , company , conn )
default :
httplib . MethodNotAllowed ( w , r , http . MethodGet , http . MethodPut )
}
default :
http . NotFound ( w , r )
}
} )
}
type taxDetailsForm struct {
BusinessName * form . Input
VATIN * form . Input
TradeName * form . Input
Phone * form . Input
Email * form . Input
Web * form . Input
Address * form . Input
City * form . Input
Province * form . Input
PostalCode * form . Input
Country * form . Select
2023-10-14 19:59:36 +00:00
RTCNumber * form . Input
2024-01-14 01:09:17 +00:00
TouristTax * form . Input
2023-08-15 20:35:21 +00:00
Currency * form . Select
DefaultLanguage * form . Select
InvoiceNumberFormat * form . Input
LegalDisclaimer * form . Input
}
func newTaxDetailsForm ( ctx context . Context , conn * database . Conn , l * locale . Locale ) * taxDetailsForm {
return & taxDetailsForm {
BusinessName : & form . Input {
Name : "business_name" ,
} ,
VATIN : & form . Input {
Name : "vatin" ,
} ,
TradeName : & form . Input {
Name : "trade_name" ,
} ,
Phone : & form . Input {
Name : "phone" ,
} ,
Email : & form . Input {
Name : "email" ,
} ,
Web : & form . Input {
Name : "web" ,
} ,
Address : & form . Input {
Name : "address" ,
} ,
City : & form . Input {
Name : "city" ,
} ,
Province : & form . Input {
Name : "province" ,
} ,
PostalCode : & form . Input {
Name : "postal_code" ,
} ,
Country : & form . Select {
Name : "country" ,
Options : form . MustGetOptions ( ctx , conn , "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name" , l . Language ) ,
} ,
2023-10-14 19:59:36 +00:00
RTCNumber : & form . Input {
Name : "rtc_number" ,
} ,
2024-01-14 01:09:17 +00:00
TouristTax : & form . Input {
Name : "tourist_tax" ,
} ,
2023-08-15 20:35:21 +00:00
Currency : & form . Select {
Name : "currency" ,
Options : form . MustGetOptions ( ctx , conn , "select currency_code, currency_symbol from currency order by currency_code" ) ,
} ,
DefaultLanguage : & form . Select {
Name : "default_language" ,
Options : form . MustGetOptions ( ctx , conn , "select lang_tag, endonym from language where selectable" ) ,
} ,
InvoiceNumberFormat : & form . Input {
Name : "invoice_number_format" ,
} ,
LegalDisclaimer : & form . Input {
Name : "legal_disclaimer" ,
} ,
}
}
func ( f * taxDetailsForm ) FillFromDatabase ( ctx context . Context , company * auth . Company , conn * database . Conn ) error {
return conn . QueryRow ( ctx , `
select business_name
, substr ( vatin : : text , 3 )
, trade_name
, phone
, email
, web
, address
, city
, province
, postal_code
2023-10-14 19:59:36 +00:00
, rtc_number
2024-01-14 01:09:17 +00:00
, to_price ( tourist_tax )
2023-08-15 20:35:21 +00:00
, array [ country_code : : text ]
, array [ currency_code : : text ]
, array [ default_lang_tag ]
, invoice_number_format
, legal_disclaimer
from company
where company . company_id = $ 1 ` , company . ID ) . Scan (
& f . BusinessName . Val ,
& f . VATIN . Val ,
& f . TradeName . Val ,
& f . Phone . Val ,
& f . Email . Val ,
& f . Web . Val ,
& f . Address . Val ,
& f . City . Val ,
& f . Province . Val ,
& f . PostalCode . Val ,
2023-10-14 19:59:36 +00:00
& f . RTCNumber . Val ,
2024-01-14 01:09:17 +00:00
& f . TouristTax . Val ,
2023-08-15 20:35:21 +00:00
& f . Country . Selected ,
& f . Currency . Selected ,
& f . DefaultLanguage . Selected ,
& f . InvoiceNumberFormat . Val ,
& f . LegalDisclaimer . Val ,
)
}
func ( f * taxDetailsForm ) Parse ( r * http . Request ) error {
if err := r . ParseForm ( ) ; err != nil {
return err
}
f . BusinessName . FillValue ( r )
f . VATIN . FillValue ( r )
f . TradeName . FillValue ( r )
f . Phone . FillValue ( r )
f . Email . FillValue ( r )
f . Web . FillValue ( r )
f . Address . FillValue ( r )
f . City . FillValue ( r )
f . Province . FillValue ( r )
f . PostalCode . FillValue ( r )
f . Country . FillValue ( r )
2023-10-14 19:59:36 +00:00
f . RTCNumber . FillValue ( r )
2024-01-14 01:09:17 +00:00
f . TouristTax . FillValue ( r )
2023-08-15 20:35:21 +00:00
f . Currency . FillValue ( r )
f . DefaultLanguage . FillValue ( r )
f . InvoiceNumberFormat . FillValue ( r )
f . LegalDisclaimer . FillValue ( r )
return nil
}
func ( f * taxDetailsForm ) Valid ( ctx context . Context , conn * database . Conn , l * locale . Locale ) ( bool , error ) {
v := form . NewValidator ( l )
var country string
if v . CheckSelectedOptions ( f . Country , l . GettextNoop ( "Selected country is not valid." ) ) {
country = f . Country . Selected [ 0 ]
}
if v . CheckRequired ( f . BusinessName , l . GettextNoop ( "Business name can not be empty." ) ) {
v . CheckMinLength ( f . BusinessName , 2 , l . GettextNoop ( "Business name must have at least two letters." ) )
}
if v . CheckRequired ( f . VATIN , l . GettextNoop ( "VAT number can not be empty." ) ) {
if _ , err := v . CheckValidVATIN ( ctx , conn , f . VATIN , country , l . GettextNoop ( "This VAT number is not valid." ) ) ; err != nil {
return false , err
}
}
if v . CheckRequired ( f . Phone , l . GettextNoop ( "Phone can not be empty." ) ) {
if _ , err := v . CheckValidPhone ( ctx , conn , f . Phone , country , l . GettextNoop ( "This phone number is not valid." ) ) ; err != nil {
return false , err
}
}
if v . CheckRequired ( f . Email , l . GettextNoop ( "Email can not be empty." ) ) {
v . CheckValidEmail ( f . Email , l . GettextNoop ( "This email is not valid. It should be like name@domain.com." ) )
}
if f . Web . Val != "" {
v . CheckValidURL ( f . Web , l . GettextNoop ( "This web address is not valid. It should be like https://domain.com/." ) )
}
v . CheckRequired ( f . Address , l . GettextNoop ( "Address can not be empty." ) )
v . CheckRequired ( f . City , l . GettextNoop ( "City can not be empty." ) )
v . CheckRequired ( f . Province , l . GettextNoop ( "Province can not be empty." ) )
2024-02-24 19:03:11 +00:00
if v . CheckRequired ( f . PostalCode , l . GettextNoop ( "Postcode can not be empty." ) ) {
if _ , err := v . CheckValidPostalCode ( ctx , conn , f . PostalCode , country , l . GettextNoop ( "This postcode is not valid." ) ) ; err != nil {
2023-08-15 20:35:21 +00:00
return false , err
}
}
2023-10-14 19:59:36 +00:00
v . CheckRequired ( f . RTCNumber , l . GettextNoop ( "RTC number can not be empty." ) )
2024-01-14 01:09:17 +00:00
if v . CheckRequired ( f . TouristTax , l . GettextNoop ( "Tourist tax can not be empty." ) ) {
if v . CheckValidDecimal ( f . TouristTax , l . GettextNoop ( "Tourist tax must be a decimal number." ) ) {
v . CheckMinDecimal ( f . TouristTax , 0.0 , l . GettextNoop ( "Tourist tax must be zero or greater." ) )
}
}
2023-08-15 20:35:21 +00:00
v . CheckSelectedOptions ( f . Currency , l . GettextNoop ( "Selected currency is not valid." ) )
v . CheckSelectedOptions ( f . DefaultLanguage , l . GettextNoop ( "Selected language is not valid." ) )
v . CheckRequired ( f . InvoiceNumberFormat , l . GettextNoop ( "Invoice number format can not be empty." ) )
return v . AllOK , nil
}
func ( f * taxDetailsForm ) MustRender ( w http . ResponseWriter , r * http . Request , user * auth . User , company * auth . Company ) {
template . MustRenderAdmin ( w , r , user , company , "taxDetails.gohtml" , f )
}
func editTaxDetails ( w http . ResponseWriter , r * http . Request , user * auth . User , company * auth . Company , conn * database . Conn ) {
f := newTaxDetailsForm ( r . Context ( ) , conn , user . Locale )
if err := f . Parse ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusBadRequest )
return
}
if err := user . VerifyCSRFToken ( r ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return
}
if ok , err := f . Valid ( r . Context ( ) , conn , user . Locale ) ; err != nil {
panic ( err )
} else if ! ok {
if ! httplib . IsHTMxRequest ( r ) {
w . WriteHeader ( http . StatusUnprocessableEntity )
}
f . MustRender ( w , r , user , company )
return
}
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
, default_lang_tag = $ 13
, invoice_number_format = $ 14
, legal_disclaimer = $ 15
2023-10-14 19:59:36 +00:00
, rtc_number = $ 16
2024-01-15 00:45:58 +00:00
, tourist_tax = parse_price ( $ 17 , $ 18 )
where company_id = $ 19
2023-08-15 20:35:21 +00:00
` ,
f . BusinessName ,
f . VATIN ,
f . TradeName ,
f . Phone ,
f . Email ,
f . Web ,
f . Address ,
f . City ,
f . Province ,
f . PostalCode ,
f . Country ,
f . Currency ,
f . DefaultLanguage ,
f . InvoiceNumberFormat ,
f . LegalDisclaimer ,
2023-10-14 19:59:36 +00:00
f . RTCNumber ,
2024-01-14 01:09:17 +00:00
f . TouristTax ,
2024-01-15 00:45:58 +00:00
company . DecimalDigits ,
2023-08-15 20:35:21 +00:00
company . ID )
httplib . Redirect ( w , r , "/admin/company" , http . StatusSeeOther )
}