Compare commits

..

No commits in common. "07705b012a0078ab6cac8546df7165e1cf16fb63" and "3a7d4548262bf67caaab01277062efe0a5a2b435" have entirely different histories.

45 changed files with 688 additions and 1549 deletions

3
.gitignore vendored
View File

@ -1,7 +1,4 @@
/.idea/
/build/
/locale/
/po/*.pot
/demo.sql
CMakeLists.txt.user*
.qmlls.ini

View File

@ -1,18 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(camper VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS
Concurrent
Quick
QuickControls2
Sql
)
qt_standard_project_setup(REQUIRES 6.5)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(src)

View File

@ -1248,15 +1248,6 @@ select set_season_range(93, daterange(make_date(extract(year from current_date):
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 9, 24), make_date(extract(year from current_date)::int, 9, 29)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 9, 29), make_date(extract(year from current_date)::int, 10, 1)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 10, 1), make_date(extract(year from current_date)::int, 10, 13)));
select set_season_range(92, daterange(make_date(extract(year from current_date)::int + 1, 4, 11), make_date(extract(year from current_date)::int + 1, 4, 22)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int + 1, 4, 22), make_date(extract(year from current_date)::int + 1, 6, 20)));
select set_season_range(92, daterange(make_date(extract(year from current_date)::int + 1, 6, 20), make_date(extract(year from current_date)::int + 1, 6, 25)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int + 1, 6, 25), make_date(extract(year from current_date)::int + 1, 7, 4)));
select set_season_range(92, daterange(make_date(extract(year from current_date)::int + 1, 7, 4), make_date(extract(year from current_date)::int + 1, 8, 25)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int + 1, 8, 25), make_date(extract(year from current_date)::int + 1, 9, 1)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int + 1, 9, 1), make_date(extract(year from current_date)::int + 1, 9, 11)));
select set_season_range(92, daterange(make_date(extract(year from current_date)::int + 1, 9, 11), make_date(extract(year from current_date)::int + 1, 9, 15)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int + 1, 9, 15), make_date(extract(year from current_date)::int + 1, 12, 9)));
select set_campsite_type_cost (slug, 92, '4.00', '7.95', '7.95', '6.40') from campsite_type where campsite_type_id = 72;
select set_campsite_type_cost (slug, 93, '2.00', '7.40', '7.40', '5.90') from campsite_type where campsite_type_id = 72;

View File

@ -1,12 +0,0 @@
-- Deploy camper:campsite_type__operating_dates to pg
-- requires: campsite_type
begin;
set search_path to camper, public;
alter table campsite_type
add column operating_dates daterange not null default 'empty'
;
commit;

View File

@ -172,7 +172,7 @@ func collectBookingEntries(ctx context.Context, conn *database.Conn, lang langua
order by lower(stay) desc
, booking_id desc
LIMIT %d
`, where, filters.PerPage()+1), args...)
`, where, filters.perPage+1), args...)
if err != nil {
return nil, err
}
@ -235,7 +235,7 @@ func (page bookingIndex) MustRender(w http.ResponseWriter, r *http.Request, user
}
ods.MustWriteResponse(w, table, user.Locale.Pgettext("bookings.ods", "filename"))
default:
if httplib.IsHTMxRequest(r) && page.Filters.Paginated() {
if httplib.IsHTMxRequest(r) && page.Filters.pagination {
template.MustRenderAdminNoLayout(w, r, user, company, "booking/results.gohtml", page)
} else {
template.MustRenderAdminFiles(w, r, user, company, page, "booking/index.gohtml", "booking/results.gohtml")

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"dev.tandem.ws/tandem/camper/pkg/auth"
@ -14,17 +13,22 @@ import (
)
type filterForm struct {
locale *locale.Locale
company *auth.Company
perPage int
pagination bool
HolderName *form.Input
BookingStatus *form.Select
FromDate *form.Input
ToDate *form.Input
Cursor *form.Cursor
Cursor *form.Input
}
func newFilterForm(ctx context.Context, conn *database.Conn, company *auth.Company, locale *locale.Locale) *filterForm {
return &filterForm{
locale: locale,
company: company,
perPage: 25,
HolderName: &form.Input{
Name: "holder_name",
},
@ -38,9 +42,8 @@ func newFilterForm(ctx context.Context, conn *database.Conn, company *auth.Compa
ToDate: &form.Input{
Name: "to_date",
},
Cursor: &form.Cursor{
Cursor: &form.Input{
Name: "cursor",
PerPage: 25,
},
}
}
@ -65,6 +68,7 @@ func (f *filterForm) Parse(r *http.Request) error {
f.FromDate.FillValue(r)
f.ToDate.FillValue(r)
f.Cursor.FillValue(r)
f.pagination = f.Cursor.Val != ""
return nil
}
@ -96,8 +100,8 @@ func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
maybeAppendWhere("lower(stay) >= $%d", f.FromDate.Val, nil)
maybeAppendWhere("lower(stay) <= $%d", f.ToDate.Val, nil)
if f.Paginated() {
params := f.Cursor.Params()
if f.Cursor.Val != "" {
params := strings.Split(f.Cursor.Val, ";")
if len(params) == 2 {
where = append(where, fmt.Sprintf("(lower(stay), booking_id) < ($%d, $%d)", len(args)+1, len(args)+2))
args = append(args, params[0])
@ -109,9 +113,14 @@ func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
}
func (f *filterForm) buildCursor(bookings []*bookingEntry) []*bookingEntry {
return form.BuildCursor(f.Cursor, bookings, func(entry *bookingEntry) []string {
return []string{entry.ArrivalDate.Format(database.ISODateFormat), strconv.Itoa(entry.ID)}
})
if len(bookings) <= f.perPage {
f.Cursor.Val = ""
return bookings
}
bookings = bookings[:f.perPage]
last := bookings[f.perPage-1]
f.Cursor.Val = fmt.Sprintf("%s;%d", last.ArrivalDate.Format(database.ISODateFormat), last.ID)
return bookings
}
func (f *filterForm) HasValue() bool {
@ -120,11 +129,3 @@ func (f *filterForm) HasValue() bool {
f.FromDate.Val != "" ||
f.ToDate.Val != ""
}
func (f *filterForm) PerPage() int {
return f.Cursor.PerPage
}
func (f *filterForm) Paginated() bool {
return f.Cursor.Pagination
}

View File

@ -59,7 +59,7 @@ type prebookingIndex struct {
}
func (page prebookingIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
if httplib.IsHTMxRequest(r) && page.Filters.Paginated() {
if httplib.IsHTMxRequest(r) && page.Filters.pagination {
template.MustRenderAdminNoLayout(w, r, user, company, "prebooking/results.gohtml", page)
} else {
template.MustRenderAdminFiles(w, r, user, company, page, "prebooking/index.gohtml", "prebooking/results.gohtml")

View File

@ -245,14 +245,14 @@ func NewDateFields(ctx context.Context, conn *database.Conn, campsiteType string
row := conn.QueryRow(ctx, `
select lower(bookable_nights),
upper(bookable_nights) - 1,
greatest(min(lower(season_range)), lower(operating_dates), current_timestamp::date),
least(max(upper(season_range)), upper(operating_dates))
greatest(min(lower(season_range)), current_timestamp::date),
max(upper(season_range))
from campsite_type
join campsite_type_cost using (campsite_type_id)
join season_calendar using (season_id)
where campsite_type.slug = $1
and season_range >> daterange(date_trunc('year', current_timestamp)::date, date_trunc('year', current_timestamp)::date + 1)
group by bookable_nights, operating_dates
group by bookable_nights;
`, campsiteType)
f := &DateFields{
ArrivalDate: &bookingDateInput{

View File

@ -109,7 +109,7 @@ func collectCustomerEntries(ctx context.Context, conn *database.Conn, company *a
where (%s)
order by name, contact_id
LIMIT %d
`, where, filters.PerPage()+1), args...)
`, where, filters.perPage+1), args...)
if err != nil {
return nil, err
}
@ -141,7 +141,7 @@ type customerIndex struct {
}
func (page *customerIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
if httplib.IsHTMxRequest(r) && page.Filters.Paginated() {
if httplib.IsHTMxRequest(r) && page.Filters.pagination {
template.MustRenderAdminNoLayout(w, r, user, company, "customer/results.gohtml", page)
} else {
template.MustRenderAdminFiles(w, r, user, company, page, "customer/index.gohtml", "customer/results.gohtml")

View File

@ -3,7 +3,6 @@ package customer
import (
"fmt"
"net/http"
"strconv"
"strings"
"dev.tandem.ws/tandem/camper/pkg/auth"
@ -12,23 +11,25 @@ import (
type filterForm struct {
company *auth.Company
perPage int
pagination bool
Name *form.Input
Email *form.Input
Cursor *form.Cursor
Cursor *form.Input
}
func newFilterForm(company *auth.Company) *filterForm {
return &filterForm{
company: company,
perPage: 25,
Name: &form.Input{
Name: "name",
},
Email: &form.Input{
Name: "email",
},
Cursor: &form.Cursor{
Cursor: &form.Input{
Name: "cursor",
PerPage: 25,
},
}
}
@ -40,6 +41,7 @@ func (f *filterForm) Parse(r *http.Request) error {
f.Name.FillValue(r)
f.Email.FillValue(r)
f.Cursor.FillValue(r)
f.pagination = f.Cursor.Val != ""
return nil
}
@ -67,8 +69,8 @@ func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
return "%" + v + "%"
})
if f.Paginated() {
params := f.Cursor.Params()
if f.Cursor.Val != "" {
params := strings.Split(f.Cursor.Val, ";")
if len(params) == 2 {
where = append(where, fmt.Sprintf("(name, contact_id) > ($%d, $%d)", len(args)+1, len(args)+2))
args = append(args, params[0])
@ -80,20 +82,17 @@ func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
}
func (f *filterForm) buildCursor(customers []*customerEntry) []*customerEntry {
return form.BuildCursor(f.Cursor, customers, func(entry *customerEntry) []string {
return []string{entry.Name, strconv.Itoa(entry.ID)}
})
if len(customers) <= f.perPage {
f.Cursor.Val = ""
return customers
}
customers = customers[:f.perPage]
last := customers[f.perPage-1]
f.Cursor.Val = fmt.Sprintf("%s;%d", last.Name, last.ID)
return customers
}
func (f *filterForm) HasValue() bool {
return f.Name.Val != "" ||
f.Email.Val != ""
}
func (f *filterForm) PerPage() int {
return f.Cursor.PerPage
}
func (f *filterForm) Paginated() bool {
return f.Cursor.Pagination
}

View File

@ -1,33 +0,0 @@
package form
import (
"net/http"
"strings"
)
type Cursor struct {
PerPage int
Pagination bool
Name string
Val string
Colspan int
}
func (cursor *Cursor) FillValue(r *http.Request) {
cursor.Val = strings.TrimSpace(r.FormValue(cursor.Name))
cursor.Pagination = cursor.Val != ""
}
func (cursor *Cursor) Params() []string {
return strings.Split(cursor.Val, ";")
}
func BuildCursor[K interface{}](cursor *Cursor, elems []K, build func(K) []string) []K {
if len(elems) <= cursor.PerPage {
cursor.Val = ""
return elems
}
elems = elems[:cursor.PerPage]
cursor.Val = strings.Join(build(elems[cursor.PerPage-1]), ";")
return elems
}

View File

@ -148,13 +148,14 @@ type IndexEntry struct {
}
func serveInvoiceIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
filters := newFilterForm(r.Context(), conn, company, user.Locale)
filters := newInvoiceFilterForm(r.Context(), conn, company, user.Locale)
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
page := &invoiceIndex{
Invoices: filters.buildCursor(mustCollectInvoiceEntries(r.Context(), conn, user.Locale, filters)),
Invoices: mustCollectInvoiceEntries(r.Context(), conn, user.Locale, filters),
TotalAmount: mustComputeInvoicesTotalAmount(r.Context(), conn, filters),
Filters: filters,
InvoiceStatuses: mustCollectInvoiceStatuses(r.Context(), conn, user.Locale),
}
@ -163,19 +164,16 @@ func serveInvoiceIndex(w http.ResponseWriter, r *http.Request, user *auth.User,
type invoiceIndex struct {
Invoices []*IndexEntry
Filters *filterForm
TotalAmount string
Filters *invoiceFilterForm
InvoiceStatuses map[string]string
}
func (page *invoiceIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
if httplib.IsHTMxRequest(r) && page.Filters.Paginated() {
template.MustRenderAdminNoLayout(w, r, user, company, "invoice/results.gohtml", page)
} else {
template.MustRenderAdminFiles(w, r, user, company, page, "invoice/index.gohtml", "invoice/results.gohtml")
}
template.MustRenderAdmin(w, r, user, company, "invoice/index.gohtml", page)
}
func mustCollectInvoiceEntries(ctx context.Context, conn *database.Conn, locale *locale.Locale, filters *filterForm) []*IndexEntry {
func mustCollectInvoiceEntries(ctx context.Context, conn *database.Conn, locale *locale.Locale, filters *invoiceFilterForm) []*IndexEntry {
where, args := filters.BuildQuery([]interface{}{locale.Language.String()})
rows, err := conn.Query(ctx, fmt.Sprintf(`
select invoice_id
@ -194,8 +192,7 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *database.Conn, locale
where (%s)
order by invoice_date desc
, invoice_number desc
limit %d
`, where, filters.PerPage()+1), args...)
`, where), args...)
if err != nil {
panic(err)
}
@ -216,6 +213,25 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *database.Conn, locale
return entries
}
func mustComputeInvoicesTotalAmount(ctx context.Context, conn *database.Conn, filters *invoiceFilterForm) string {
where, args := filters.BuildQuery(nil)
text, err := conn.GetText(ctx, fmt.Sprintf(`
select to_price(sum(total)::integer, decimal_digits)
from invoice
join invoice_amount using (invoice_id)
join currency using (currency_code)
where (%s)
group by decimal_digits
`, where), args...)
if err != nil {
if database.ErrorIsNotFound(err) {
return "0.0"
}
panic(err)
}
return text
}
func mustCollectInvoiceStatuses(ctx context.Context, conn *database.Conn, locale *locale.Locale) map[string]string {
rows, err := conn.Query(ctx, `
select invoice_status.invoice_status
@ -245,6 +261,88 @@ func mustCollectInvoiceStatuses(ctx context.Context, conn *database.Conn, locale
return statuses
}
type invoiceFilterForm struct {
locale *locale.Locale
company *auth.Company
Customer *form.Select
InvoiceStatus *form.Select
InvoiceNumber *form.Input
FromDate *form.Input
ToDate *form.Input
}
func newInvoiceFilterForm(ctx context.Context, conn *database.Conn, company *auth.Company, locale *locale.Locale) *invoiceFilterForm {
return &invoiceFilterForm{
locale: locale,
company: company,
Customer: &form.Select{
Name: "customer",
Options: mustGetContactOptions(ctx, conn, company),
},
InvoiceStatus: &form.Select{
Name: "invoice_status",
Options: mustGetInvoiceStatusOptions(ctx, conn, locale),
},
InvoiceNumber: &form.Input{
Name: "number",
},
FromDate: &form.Input{
Name: "from_date",
},
ToDate: &form.Input{
Name: "to_date",
},
}
}
func (f *invoiceFilterForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.Customer.FillValue(r)
f.InvoiceStatus.FillValue(r)
f.InvoiceNumber.FillValue(r)
f.FromDate.FillValue(r)
f.ToDate.FillValue(r)
return nil
}
func (f *invoiceFilterForm) BuildQuery(args []interface{}) (string, []interface{}) {
var where []string
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
appendWhere("invoice.company_id = $%d", f.company.ID)
maybeAppendWhere("contact_id = $%d", f.Customer.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(f.Customer.Selected[0])
return customerId
})
maybeAppendWhere("invoice.invoice_status = $%d", f.InvoiceStatus.String(), nil)
maybeAppendWhere("invoice_number = $%d", f.InvoiceNumber.Val, nil)
maybeAppendWhere("invoice_date >= $%d", f.FromDate.Val, nil)
maybeAppendWhere("invoice_date <= $%d", f.ToDate.Val, nil)
return strings.Join(where, ") AND ("), args
}
func (f *invoiceFilterForm) HasValue() bool {
return (len(f.Customer.Selected) > 0 && f.Customer.Selected[0] != "") ||
(len(f.InvoiceStatus.Selected) > 0 && f.InvoiceStatus.Selected[0] != "") ||
f.InvoiceNumber.Val != "" ||
f.FromDate.Val != "" ||
f.ToDate.Val != ""
}
func serveInvoice(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, slug string) {
pdf := false
if strings.HasSuffix(slug, ".pdf") {
@ -584,7 +682,7 @@ func handleBatchAction(w http.ResponseWriter, r *http.Request, user *auth.User,
panic(err)
}
case "export":
filters := newFilterForm(r.Context(), conn, company, user.Locale)
filters := newInvoiceFilterForm(r.Context(), conn, company, user.Locale)
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return

View File

@ -1,124 +0,0 @@
package invoice
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
"dev.tandem.ws/tandem/camper/pkg/locale"
)
type filterForm struct {
company *auth.Company
Customer *form.Select
InvoiceStatus *form.Select
InvoiceNumber *form.Input
FromDate *form.Input
ToDate *form.Input
Cursor *form.Cursor
}
func newFilterForm(ctx context.Context, conn *database.Conn, company *auth.Company, locale *locale.Locale) *filterForm {
return &filterForm{
company: company,
Customer: &form.Select{
Name: "customer",
Options: mustGetContactOptions(ctx, conn, company),
},
InvoiceStatus: &form.Select{
Name: "invoice_status",
Options: mustGetInvoiceStatusOptions(ctx, conn, locale),
},
InvoiceNumber: &form.Input{
Name: "number",
},
FromDate: &form.Input{
Name: "from_date",
},
ToDate: &form.Input{
Name: "to_date",
},
Cursor: &form.Cursor{
Name: "cursor",
PerPage: 25,
},
}
}
func (f *filterForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.Customer.FillValue(r)
f.InvoiceStatus.FillValue(r)
f.InvoiceNumber.FillValue(r)
f.FromDate.FillValue(r)
f.ToDate.FillValue(r)
f.Cursor.FillValue(r)
return nil
}
func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
var where []string
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
appendWhere("invoice.company_id = $%d", f.company.ID)
maybeAppendWhere("contact_id = $%d", f.Customer.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(f.Customer.Selected[0])
return customerId
})
maybeAppendWhere("invoice.invoice_status = $%d", f.InvoiceStatus.String(), nil)
maybeAppendWhere("invoice_number = $%d", f.InvoiceNumber.Val, nil)
maybeAppendWhere("invoice_date >= $%d", f.FromDate.Val, nil)
maybeAppendWhere("invoice_date <= $%d", f.ToDate.Val, nil)
if f.Paginated() {
params := f.Cursor.Params()
if len(params) == 2 {
where = append(where, fmt.Sprintf("(invoice_date, invoice_number) < ($%d, $%d)", len(args)+1, len(args)+2))
args = append(args, params[0])
args = append(args, params[1])
}
}
return strings.Join(where, ") AND ("), args
}
func (f *filterForm) buildCursor(customers []*IndexEntry) []*IndexEntry {
return form.BuildCursor(f.Cursor, customers, func(entry *IndexEntry) []string {
return []string{entry.Date.Format(database.ISODateFormat), entry.Number}
})
}
func (f *filterForm) HasValue() bool {
return (len(f.Customer.Selected) > 0 && f.Customer.Selected[0] != "") ||
(len(f.InvoiceStatus.Selected) > 0 && f.InvoiceStatus.Selected[0] != "") ||
f.InvoiceNumber.Val != "" ||
f.FromDate.Val != "" ||
f.ToDate.Val != ""
}
func (f *filterForm) PerPage() int {
return f.Cursor.PerPage
}
func (f *filterForm) Paginated() bool {
return f.Cursor.Pagination
}

View File

@ -108,24 +108,17 @@ func (h *AdminHandler) paymentHandler(user *auth.User, company *auth.Company, co
}
func servePaymentIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
filters := newFilterForm(r.Context(), conn, company, user.Locale)
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
payments, err := collectPaymentEntries(r.Context(), company, conn, user.Locale, filters)
payments, err := collectPaymentEntries(r.Context(), company, conn, user.Locale)
if err != nil {
panic(err)
}
page := &paymentIndex{
Payments: filters.buildCursor(payments),
Filters: filters,
Payments: payments,
}
page.MustRender(w, r, user, company)
}
type paymentEntry struct {
ID int
URL string
Reference string
DownPayment string
@ -135,11 +128,9 @@ type paymentEntry struct {
CreatedAt time.Time
}
func collectPaymentEntries(ctx context.Context, company *auth.Company, conn *database.Conn, locale *locale.Locale, filters *filterForm) ([]*paymentEntry, error) {
where, args := filters.BuildQuery([]interface{}{locale.Language})
rows, err := conn.Query(ctx, fmt.Sprintf(`
select payment_id
, '/admin/payments/' || payment.slug
func collectPaymentEntries(ctx context.Context, company *auth.Company, conn *database.Conn, locale *locale.Locale) ([]*paymentEntry, error) {
rows, err := conn.Query(ctx, `
select '/admin/payments/' || payment.slug
, payment.reference
, to_price(payment.down_payment, decimal_digits)
, to_price(total, decimal_digits)
@ -150,12 +141,10 @@ func collectPaymentEntries(ctx context.Context, company *auth.Company, conn *dat
join currency using (currency_code)
join payment_status using (payment_status)
left join payment_status_i18n on payment_status_i18n.payment_status = payment.payment_status
and payment_status_i18n.lang_tag = $1
where (%s)
and payment_status_i18n.lang_tag = $2
where company_id = $1
order by created_at desc
, payment_id
limit %d
`, where, filters.PerPage()+1), args...)
`, company.ID, locale.Language)
if err != nil {
return nil, err
}
@ -165,7 +154,6 @@ func collectPaymentEntries(ctx context.Context, company *auth.Company, conn *dat
for rows.Next() {
entry := &paymentEntry{}
if err = rows.Scan(
&entry.ID,
&entry.URL,
&entry.Reference,
&entry.DownPayment,
@ -184,15 +172,10 @@ func collectPaymentEntries(ctx context.Context, company *auth.Company, conn *dat
type paymentIndex struct {
Payments []*paymentEntry
Filters *filterForm
}
func (page *paymentIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
if httplib.IsHTMxRequest(r) && page.Filters.Paginated() {
template.MustRenderAdminNoLayout(w, r, user, company, "payment/results.gohtml", page)
} else {
template.MustRenderAdminFiles(w, r, user, company, page, "payment/index.gohtml", "payment/results.gohtml")
}
template.MustRenderAdmin(w, r, user, company, "payment/index.gohtml", page)
}
type paymentDetails struct {

View File

@ -1,125 +0,0 @@
package payment
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
"dev.tandem.ws/tandem/camper/pkg/locale"
)
type filterForm struct {
company *auth.Company
PaymentStatus *form.Select
Reference *form.Input
FromDate *form.Input
ToDate *form.Input
Cursor *form.Cursor
}
func newFilterForm(ctx context.Context, conn *database.Conn, company *auth.Company, locale *locale.Locale) *filterForm {
return &filterForm{
company: company,
PaymentStatus: &form.Select{
Name: "payment_status",
Options: mustGetPaymentStatusOptions(ctx, conn, locale),
},
Reference: &form.Input{
Name: "reference",
},
FromDate: &form.Input{
Name: "from_date",
},
ToDate: &form.Input{
Name: "to_date",
},
Cursor: &form.Cursor{
Name: "cursor",
PerPage: 25,
},
}
}
func mustGetPaymentStatusOptions(ctx context.Context, conn *database.Conn, locale *locale.Locale) []*form.Option {
return form.MustGetOptions(ctx, conn, `
select payment_status.payment_status
, isi18n.name
from payment_status
join payment_status_i18n isi18n using(payment_status)
where isi18n.lang_tag = $1
order by payment_status`, locale.Language)
}
func (f *filterForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.PaymentStatus.FillValue(r)
f.Reference.FillValue(r)
f.FromDate.FillValue(r)
f.ToDate.FillValue(r)
f.Cursor.FillValue(r)
return nil
}
func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
var where []string
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
appendWhere("payment.company_id = $%d", f.company.ID)
maybeAppendWhere("payment.payment_status = $%d", f.PaymentStatus.String(), nil)
maybeAppendWhere("payment.reference like $%d", f.Reference.Val, func(v string) interface{} {
return "%" + v
})
maybeAppendWhere("payment.created_at >= $%d", f.FromDate.Val, nil)
maybeAppendWhere("payment.created_at <= $%d", f.ToDate.Val, nil)
if f.Paginated() {
params := f.Cursor.Params()
if len(params) == 2 {
where = append(where, fmt.Sprintf("(payment.created_at, payment_id) < ($%d, $%d)", len(args)+1, len(args)+2))
args = append(args, params[0])
args = append(args, params[1])
}
}
return strings.Join(where, ") AND ("), args
}
func (f *filterForm) buildCursor(customers []*paymentEntry) []*paymentEntry {
return form.BuildCursor(f.Cursor, customers, func(entry *paymentEntry) []string {
return []string{entry.CreatedAt.Format(database.ISODateTimeFormat), strconv.Itoa(entry.ID)}
})
}
func (f *filterForm) HasValue() bool {
return (len(f.PaymentStatus.Selected) > 0 && f.PaymentStatus.Selected[0] != "") ||
f.Reference.Val != "" ||
f.FromDate.Val != "" ||
f.ToDate.Val != ""
}
func (f *filterForm) PerPage() int {
return f.Cursor.PerPage
}
func (f *filterForm) Paginated() bool {
return f.Cursor.Pagination
}

View File

@ -24,7 +24,6 @@ import (
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/build"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
)
@ -162,10 +161,6 @@ func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templ
return int(num)
},
"slugify": Slugify,
"colspan": func(colspan int, cursor *form.Cursor) *form.Cursor {
cursor.Colspan = colspan
return cursor
},
})
templates = append(templates, "form.gohtml")
files := make([]string, len(templates))

View File

@ -1,98 +0,0 @@
package user
import (
"fmt"
"net/http"
"strconv"
"strings"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
)
type filterForm struct {
company *auth.Company
FromDate *form.Input
ToDate *form.Input
Cursor *form.Cursor
}
func newFilterForm() *filterForm {
return &filterForm{
FromDate: &form.Input{
Name: "from_date",
},
ToDate: &form.Input{
Name: "to_date",
},
Cursor: &form.Cursor{
Name: "cursor",
PerPage: 5,
},
}
}
func (f *filterForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.FromDate.FillValue(r)
f.ToDate.FillValue(r)
f.Cursor.FillValue(r)
return nil
}
func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
var where []string
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
maybeAppendWhere("attempted_at >= $%d", f.FromDate.Val, nil)
maybeAppendWhere("attempted_at <= $%d", f.ToDate.Val, nil)
if f.Paginated() {
params := f.Cursor.Params()
if len(params) == 2 {
where = append(where, fmt.Sprintf("(attempted_at, attempt_id) < ($%d, $%d)", len(args)+1, len(args)+2))
args = append(args, params[0])
args = append(args, params[1])
}
}
if len(where) == 0 {
return "1=1", args
}
return strings.Join(where, ") AND ("), args
}
func (f *filterForm) buildCursor(customers []*loginAttemptEntry) []*loginAttemptEntry {
return form.BuildCursor(f.Cursor, customers, func(entry *loginAttemptEntry) []string {
return []string{entry.Date.Format(database.ISODateTimeFormat), strconv.Itoa(entry.ID)}
})
}
func (f *filterForm) HasValue() bool {
return f.FromDate.Val != "" ||
f.ToDate.Val != ""
}
func (f *filterForm) PerPage() int {
return f.Cursor.PerPage
}
func (f *filterForm) Paginated() bool {
return f.Cursor.Pagination
}

View File

@ -2,8 +2,6 @@ package user
import (
"context"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"fmt"
"net/http"
"time"
@ -13,44 +11,32 @@ import (
)
func serveLoginAttemptIndex(w http.ResponseWriter, r *http.Request, loginAttempt *auth.User, company *auth.Company, conn *database.Conn) {
filters := newFilterForm()
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
loginAttempts, err := collectLoginAttemptEntries(r.Context(), conn, filters)
loginAttempts, err := collectLoginAttemptEntries(r.Context(), conn)
if err != nil {
panic(err)
}
index := &loginAttemptIndex{
Attempts: filters.buildCursor(loginAttempts),
Filters: filters,
}
index.MustRender(w, r, loginAttempt, company)
loginAttempts.MustRender(w, r, loginAttempt, company)
}
func collectLoginAttemptEntries(ctx context.Context, conn *database.Conn, filters *filterForm) ([]*loginAttemptEntry, error) {
where, args := filters.BuildQuery(nil)
rows, err := conn.Query(ctx, fmt.Sprintf(`
select attempt_id
, user_name
func collectLoginAttemptEntries(ctx context.Context, conn *database.Conn) (loginAttemptIndex, error) {
rows, err := conn.Query(ctx, `
select user_name
, host(ip_address)
, attempted_at
, success
from company_login_attempt
where (%s)
order by attempted_at desc, attempt_id desc
limit %d
`, where, filters.PerPage()+1), args...)
order by attempted_at desc
limit 500
`)
if err != nil {
return nil, err
}
defer rows.Close()
var entries []*loginAttemptEntry
var entries loginAttemptIndex
for rows.Next() {
entry := &loginAttemptEntry{}
if err = rows.Scan(&entry.ID, &entry.UserName, &entry.IPAddress, &entry.Date, &entry.Success); err != nil {
if err = rows.Scan(&entry.UserName, &entry.IPAddress, &entry.Date, &entry.Success); err != nil {
return nil, err
}
entries = append(entries, entry)
@ -60,22 +46,14 @@ func collectLoginAttemptEntries(ctx context.Context, conn *database.Conn, filter
}
type loginAttemptEntry struct {
ID int
UserName string
IPAddress string
Date time.Time
Success bool
}
type loginAttemptIndex struct {
Attempts []*loginAttemptEntry
Filters *filterForm
}
type loginAttemptIndex []*loginAttemptEntry
func (page *loginAttemptIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
if httplib.IsHTMxRequest(r) && page.Filters.Paginated() {
template.MustRenderAdminNoLayout(w, r, user, company, "user/results.gohtml", page)
} else {
template.MustRenderAdminFiles(w, r, user, company, page, "user/login-attempts.gohtml", "user/results.gohtml")
}
func (page *loginAttemptIndex) MustRender(w http.ResponseWriter, r *http.Request, loginAttempt *auth.User, company *auth.Company) {
template.MustRenderAdmin(w, r, loginAttempt, company, "user/login-attempts.gohtml", page)
}

275
po/ca.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-05-13 10:37+0200\n"
"POT-Creation-Date: 2024-05-03 17:19+0200\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -78,7 +78,7 @@ msgid "Payment"
msgstr "Pagament"
#: web/templates/mail/payment/details.gotxt:9
#: web/templates/admin/payment/index.gohtml:73
#: web/templates/admin/payment/index.gohtml:21
#: web/templates/admin/payment/details.gohtml:52
#: web/templates/admin/prebooking/index.gohtml:59
#: web/templates/admin/booking/index.gohtml:73
@ -87,7 +87,7 @@ msgid "Reference"
msgstr "Referència"
#: web/templates/mail/payment/details.gotxt:10
#: web/templates/admin/payment/index.gohtml:74
#: web/templates/admin/payment/index.gohtml:22
#: web/templates/admin/payment/details.gohtml:56
#: web/templates/admin/booking/index.gohtml:77
msgctxt "header"
@ -131,14 +131,14 @@ msgstr "Preferències dàrea"
#: web/templates/mail/payment/details.gotxt:18
#: web/templates/admin/payment/details.gohtml:82
msgctxt "input"
msgid "ACSI / ANWB card?"
msgstr "Targeta ACSI / ANWB?"
msgid "ACSI card?"
msgstr "Targeta ACSI?"
#: web/templates/mail/payment/details.gotxt:18
#: web/templates/admin/payment/details.gohtml:83
#: web/templates/admin/campsite/type/index.gohtml:53
#: web/templates/admin/season/index.gohtml:44
#: web/templates/admin/user/results.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:31
#: web/templates/admin/amenity/index.gohtml:40
msgid "Yes"
msgstr "Sí"
@ -147,7 +147,7 @@ msgstr "Sí"
#: web/templates/admin/payment/details.gohtml:83
#: web/templates/admin/campsite/type/index.gohtml:53
#: web/templates/admin/season/index.gohtml:44
#: web/templates/admin/user/results.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:31
#: web/templates/admin/amenity/index.gohtml:40
msgid "No"
msgstr "No"
@ -179,7 +179,7 @@ msgstr "Nits"
#: web/templates/mail/payment/details.gotxt:22
#: web/templates/public/booking/fields.gohtml:60
#: web/templates/admin/payment/details.gohtml:98
#: web/templates/admin/booking/fields.gohtml:93 pkg/invoice/admin.go:964
#: web/templates/admin/booking/fields.gohtml:93 pkg/invoice/admin.go:1062
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adults de 17 anys o més"
@ -187,7 +187,7 @@ msgstr "Adults de 17 anys o més"
#: web/templates/mail/payment/details.gotxt:23
#: web/templates/public/booking/fields.gohtml:71
#: web/templates/admin/payment/details.gohtml:102
#: web/templates/admin/booking/fields.gohtml:109 pkg/invoice/admin.go:965
#: web/templates/admin/booking/fields.gohtml:109 pkg/invoice/admin.go:1063
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents dentre 11 i 16 anys"
@ -195,7 +195,7 @@ msgstr "Adolescents dentre 11 i 16 anys"
#: web/templates/mail/payment/details.gotxt:24
#: web/templates/public/booking/fields.gohtml:82
#: web/templates/admin/payment/details.gohtml:106
#: web/templates/admin/booking/fields.gohtml:125 pkg/invoice/admin.go:966
#: web/templates/admin/booking/fields.gohtml:125 pkg/invoice/admin.go:1064
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Nens dentre 2 i 10 anys"
@ -203,14 +203,14 @@ msgstr "Nens dentre 2 i 10 anys"
#: web/templates/mail/payment/details.gotxt:25
#: web/templates/public/booking/fields.gohtml:100
#: web/templates/admin/payment/details.gohtml:110
#: web/templates/admin/booking/fields.gohtml:140 pkg/invoice/admin.go:967
#: web/templates/admin/booking/fields.gohtml:140 pkg/invoice/admin.go:1065
msgctxt "input"
msgid "Dogs"
msgstr "Gossos"
#: web/templates/mail/payment/details.gotxt:26
#: web/templates/admin/payment/details.gohtml:114
#: web/templates/admin/booking/fields.gohtml:167 pkg/invoice/admin.go:968
#: web/templates/admin/booking/fields.gohtml:167 pkg/invoice/admin.go:1066
#: pkg/booking/cart.go:242
msgctxt "cart"
msgid "Tourist tax"
@ -293,7 +293,6 @@ msgstr "País"
#: web/templates/mail/payment/details.gotxt:46
#: web/templates/public/booking/fields.gohtml:201
#: web/templates/admin/payment/details.gohtml:163
#: web/templates/admin/customer/index.gohtml:33
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
@ -419,7 +418,7 @@ msgid "Order Number"
msgstr "Número de comanda"
#: web/templates/public/payment/details.gohtml:8
#: web/templates/admin/invoice/index.gohtml:104
#: web/templates/admin/invoice/index.gohtml:103
#: web/templates/admin/invoice/view.gohtml:26
msgctxt "title"
msgid "Date"
@ -597,12 +596,6 @@ msgctxt "action"
msgid "Filters"
msgstr "Filtres"
#: web/templates/public/form.gohtml:93 web/templates/admin/form.gohtml:93
#: web/templates/admin/prebooking/results.gohtml:20
msgctxt "action"
msgid "Load more"
msgstr "Carregan més"
#: web/templates/public/campsite/type.gohtml:49
#: web/templates/public/booking/fields.gohtml:278
msgctxt "action"
@ -1043,8 +1036,8 @@ msgstr "Esculli un país"
#: web/templates/public/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:259
msgctxt "input"
msgid "ACSI / ANWB card? (optional)"
msgstr "Targeta ACSI / ANWB? (opcional)"
msgid "ACSI card? (optional)"
msgstr "Targeta ACSI? (opcional)"
#: web/templates/public/booking/fields.gohtml:255
msgctxt "input"
@ -1114,69 +1107,25 @@ msgctxt "action"
msgid "Save changes"
msgstr "Desa els canvis"
#: web/templates/admin/payment/index.gohtml:24
msgctxt "input"
msgid "Payment status"
msgstr "Estat del pagament"
#: web/templates/admin/payment/index.gohtml:28
#: web/templates/admin/invoice/index.gohtml:55
#: web/templates/admin/booking/index.gohtml:38
msgid "All statuses"
msgstr "Tots els estats"
#: web/templates/admin/payment/index.gohtml:36
#: web/templates/admin/invoice/index.gohtml:63
#: web/templates/admin/prebooking/index.gohtml:32
#: web/templates/admin/user/login-attempts.gohtml:24
#: web/templates/admin/booking/index.gohtml:46
msgctxt "input"
msgid "From date"
msgstr "De la data"
#: web/templates/admin/payment/index.gohtml:45
#: web/templates/admin/invoice/index.gohtml:72
#: web/templates/admin/prebooking/index.gohtml:41
#: web/templates/admin/user/login-attempts.gohtml:33
#: web/templates/admin/booking/index.gohtml:55
msgctxt "input"
msgid "To date"
msgstr "A la data"
#: web/templates/admin/payment/index.gohtml:54
msgctxt "input"
msgid "Reference"
msgstr "Referència"
#: web/templates/admin/payment/index.gohtml:64
#: web/templates/admin/customer/index.gohtml:43
#: web/templates/admin/invoice/index.gohtml:94
#: web/templates/admin/prebooking/index.gohtml:51
#: web/templates/admin/user/login-attempts.gohtml:43
#: web/templates/admin/booking/index.gohtml:65
msgctxt "action"
msgid "Reset"
msgstr "Restableix"
#: web/templates/admin/payment/index.gohtml:72
#: web/templates/admin/user/login-attempts.gohtml:51
#: web/templates/admin/payment/index.gohtml:20
#: web/templates/admin/user/login-attempts.gohtml:19
msgctxt "header"
msgid "Date"
msgstr "Data"
#: web/templates/admin/payment/index.gohtml:75
#: web/templates/admin/payment/index.gohtml:23
msgctxt "header"
msgid "Down payment"
msgstr "A compte"
#: web/templates/admin/payment/index.gohtml:76
#: web/templates/admin/payment/index.gohtml:24
#: web/templates/admin/booking/fields.gohtml:75
#: web/templates/admin/booking/fields.gohtml:173
msgctxt "header"
msgid "Total"
msgstr "Total"
#: web/templates/admin/payment/index.gohtml:84
#: web/templates/admin/payment/index.gohtml:40
msgid "No payments found."
msgstr "No sha trobat cap pagament."
@ -1234,7 +1183,6 @@ msgstr "Àlies"
#: web/templates/admin/campsite/type/form.gohtml:51
#: web/templates/admin/campsite/type/option/form.gohtml:41
#: web/templates/admin/season/form.gohtml:50
#: web/templates/admin/customer/index.gohtml:24
#: web/templates/admin/invoice/product-form.gohtml:16
#: web/templates/admin/services/form.gohtml:53
#: web/templates/admin/profile.gohtml:29
@ -1307,7 +1255,7 @@ msgstr "Afegeix text legal"
#: web/templates/admin/campsite/type/option/index.gohtml:30
#: web/templates/admin/campsite/type/index.gohtml:29
#: web/templates/admin/season/index.gohtml:29
#: web/templates/admin/customer/index.gohtml:51
#: web/templates/admin/customer/index.gohtml:19
#: web/templates/admin/user/index.gohtml:20
#: web/templates/admin/surroundings/index.gohtml:83
#: web/templates/admin/amenity/feature/index.gohtml:30
@ -1912,7 +1860,7 @@ msgid "New Customer"
msgstr "Nou client"
#: web/templates/admin/customer/form.gohtml:15
#: web/templates/admin/invoice/index.gohtml:106
#: web/templates/admin/invoice/index.gohtml:105
msgctxt "title"
msgid "Customer"
msgstr "Client"
@ -1969,19 +1917,19 @@ msgctxt "action"
msgid "Add Customer"
msgstr "Afegeix client"
#: web/templates/admin/customer/index.gohtml:52
#: web/templates/admin/user/login-attempts.gohtml:52
#: web/templates/admin/customer/index.gohtml:20
#: web/templates/admin/user/login-attempts.gohtml:20
#: web/templates/admin/user/index.gohtml:21
msgctxt "header"
msgid "Email"
msgstr "Correu-e"
#: web/templates/admin/customer/index.gohtml:53
#: web/templates/admin/customer/index.gohtml:21
msgctxt "header"
msgid "Phone"
msgstr "Telèfon"
#: web/templates/admin/customer/index.gohtml:61
#: web/templates/admin/customer/index.gohtml:33
msgid "No customer found."
msgstr "No sha trobat cap client."
@ -2099,6 +2047,25 @@ msgstr "Client"
msgid "All customers"
msgstr "Tots els clients"
#: web/templates/admin/invoice/index.gohtml:55
#: web/templates/admin/booking/index.gohtml:38
msgid "All statuses"
msgstr "Tots els estats"
#: web/templates/admin/invoice/index.gohtml:63
#: web/templates/admin/prebooking/index.gohtml:32
#: web/templates/admin/booking/index.gohtml:46
msgctxt "input"
msgid "From date"
msgstr "De la data"
#: web/templates/admin/invoice/index.gohtml:72
#: web/templates/admin/prebooking/index.gohtml:41
#: web/templates/admin/booking/index.gohtml:55
msgctxt "input"
msgid "To date"
msgstr "A la data"
#: web/templates/admin/invoice/index.gohtml:81
msgctxt "input"
msgid "Invoice number"
@ -2109,39 +2076,60 @@ msgctxt "action"
msgid "Filter"
msgstr "Filtra"
#: web/templates/admin/invoice/index.gohtml:94
#: web/templates/admin/prebooking/index.gohtml:51
#: web/templates/admin/booking/index.gohtml:65
msgctxt "action"
msgid "Reset"
msgstr "Restableix"
#: web/templates/admin/invoice/index.gohtml:97
msgctxt "action"
msgid "Add invoice"
msgstr "Afegeix factura"
#: web/templates/admin/invoice/index.gohtml:103
#: web/templates/admin/invoice/index.gohtml:102
msgctxt "invoice"
msgid "All"
msgstr "Totes"
#: web/templates/admin/invoice/index.gohtml:105
#: web/templates/admin/invoice/index.gohtml:104
msgctxt "title"
msgid "Invoice Num."
msgstr "Núm. de factura"
#: web/templates/admin/invoice/index.gohtml:107
#: web/templates/admin/invoice/index.gohtml:106
msgctxt "title"
msgid "Status"
msgstr "Estat"
#: web/templates/admin/invoice/index.gohtml:108
#: web/templates/admin/invoice/index.gohtml:107
msgctxt "title"
msgid "Download"
msgstr "Descàrrega"
#: web/templates/admin/invoice/index.gohtml:109
#: web/templates/admin/invoice/index.gohtml:108
msgctxt "title"
msgid "Amount"
msgstr "Import"
#: web/templates/admin/invoice/index.gohtml:117
msgid "No invoices found."
msgstr "No sha trobat cap factura."
#: web/templates/admin/invoice/index.gohtml:115
msgctxt "action"
msgid "Select invoice %v"
msgstr "Selecciona la factura %v"
#: web/templates/admin/invoice/index.gohtml:144
msgctxt "action"
msgid "Download invoice %s"
msgstr "Descarrega la factura %s"
#: web/templates/admin/invoice/index.gohtml:154
msgid "No invoices added yet."
msgstr "No sha afegit cap factura encara."
#: web/templates/admin/invoice/index.gohtml:161
msgid "Total"
msgstr "Total"
#: web/templates/admin/invoice/view.gohtml:2
msgctxt "title"
@ -2183,16 +2171,6 @@ msgctxt "title"
msgid "Tax Base"
msgstr "Base imposable"
#: web/templates/admin/invoice/results.gohtml:3
msgctxt "action"
msgid "Select invoice %v"
msgstr "Selecciona la factura %v"
#: web/templates/admin/invoice/results.gohtml:32
msgctxt "action"
msgid "Download invoice %s"
msgstr "Descarrega la factura %s"
#: web/templates/admin/prebooking/index.gohtml:6
#: web/templates/admin/layout.gohtml:92
#: web/templates/admin/booking/form.gohtml:20
@ -2238,6 +2216,12 @@ msgstr "Nom del titular"
msgid "No prebooking found."
msgstr "No sha trobat cap pre-reserva."
#: web/templates/admin/prebooking/results.gohtml:20
#: web/templates/admin/booking/results.gohtml:23
msgctxt "action"
msgid "Load more"
msgstr "Carregan més"
#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:18
msgctxt "title"
msgid "Login"
@ -2323,7 +2307,7 @@ msgid "Password Confirmation"
msgstr "Confirmació de la contrasenya"
#: web/templates/admin/user/login-attempts.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:46
#: web/templates/admin/user/login-attempts.gohtml:15
msgctxt "title"
msgid "Login Attempts"
msgstr "Intents dentrada"
@ -2336,20 +2320,16 @@ msgctxt "title"
msgid "Users"
msgstr "Usuaris"
#: web/templates/admin/user/login-attempts.gohtml:53
#: web/templates/admin/user/login-attempts.gohtml:21
msgctxt "header"
msgid "IP Address"
msgstr "Adreça IP"
#: web/templates/admin/user/login-attempts.gohtml:54
#: web/templates/admin/user/login-attempts.gohtml:22
msgctxt "header"
msgid "Success"
msgstr "Èxit"
#: web/templates/admin/user/login-attempts.gohtml:62
msgid "No logging attempts found."
msgstr "No sha trobat cap intent dentrada."
#: web/templates/admin/user/index.gohtml:14
msgctxt "action"
msgid "Add User"
@ -2721,7 +2701,7 @@ msgctxt "header"
msgid "Decription"
msgstr "Descripció"
#: web/templates/admin/booking/fields.gohtml:81 pkg/invoice/admin.go:963
#: web/templates/admin/booking/fields.gohtml:81 pkg/invoice/admin.go:1061
#: pkg/booking/cart.go:232
msgctxt "cart"
msgid "Night"
@ -2920,7 +2900,7 @@ msgstr "Rebut amb èxit el pagament de la reserva"
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
#: pkg/campsite/feature.go:269 pkg/season/admin.go:411
#: pkg/invoice/admin.go:1060 pkg/services/admin.go:316
#: pkg/invoice/admin.go:1158 pkg/services/admin.go:316
#: pkg/surroundings/admin.go:340 pkg/amenity/feature.go:269
#: pkg/amenity/admin.go:283
msgid "Name can not be empty."
@ -2961,8 +2941,8 @@ msgstr "La imatge de la diapositiva ha de ser un mèdia de tipus imatge."
msgid "Email can not be empty."
msgstr "No podeu deixar el correu-e en blanc."
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:361
#: pkg/company/admin.go:225 pkg/booking/admin.go:479 pkg/booking/public.go:593
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:345
#: pkg/company/admin.go:225 pkg/booking/admin.go:597 pkg/booking/public.go:593
msgid "This email is not valid. It should be like name@domain.com."
msgstr "Aquest correu-e no és vàlid. Hauria de ser similar a nom@domini.com."
@ -3019,15 +2999,15 @@ msgstr "El valor del màxim ha de ser un número enter."
msgid "Maximum must be equal or greater than minimum."
msgstr "El valor del màxim ha de ser igual o superir al del mínim."
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1061
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1159
msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc."
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1062
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1160
msgid "Price must be a decimal number."
msgstr "El preu ha de ser un número decimal."
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1063
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1161
msgid "Price must be zero or greater."
msgstr "El preu ha de ser com a mínim zero."
@ -3173,7 +3153,7 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Mainada (entre 2 i 10 anys)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:455 pkg/booking/public.go:177
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:573 pkg/booking/public.go:177
#: pkg/booking/public.go:232
msgid "Selected campsite type is not valid."
msgstr "El tipus dallotjament escollit no és vàlid."
@ -3211,129 +3191,129 @@ msgstr "No podeu deixar la data de fi en blanc."
msgid "End date must be a valid date."
msgstr "La data de fi ha de ser una data vàlida."
#: pkg/customer/admin.go:342 pkg/company/admin.go:207
#: pkg/customer/admin.go:326 pkg/company/admin.go:207
#: pkg/booking/checkin.go:300 pkg/booking/public.go:577
msgid "Selected country is not valid."
msgstr "El país escollit no és vàlid."
#: pkg/customer/admin.go:346 pkg/booking/checkin.go:284
#: pkg/customer/admin.go:330 pkg/booking/checkin.go:284
msgid "Selected ID document type is not valid."
msgstr "El tipus de document didentitat escollit no és vàlid."
#: pkg/customer/admin.go:347 pkg/booking/checkin.go:285
#: pkg/customer/admin.go:331 pkg/booking/checkin.go:285
msgid "ID document number can not be empty."
msgstr "No podeu deixar el número document didentitat en blanc."
#: pkg/customer/admin.go:349 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:467
#: pkg/customer/admin.go:333 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:585
#: pkg/booking/public.go:581
msgid "Full name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc."
#: pkg/customer/admin.go:350 pkg/booking/admin.go:468 pkg/booking/public.go:582
#: pkg/customer/admin.go:334 pkg/booking/admin.go:586 pkg/booking/public.go:582
msgid "Full name must have at least one letter."
msgstr "El nom i els cognoms han de tenir com a mínim una lletra."
#: pkg/customer/admin.go:353 pkg/company/admin.go:230 pkg/booking/public.go:585
#: pkg/customer/admin.go:337 pkg/company/admin.go:230 pkg/booking/public.go:585
msgid "Address can not be empty."
msgstr "No podeu deixar ladreça en blanc."
#: pkg/customer/admin.go:354 pkg/booking/public.go:586
#: pkg/customer/admin.go:338 pkg/booking/public.go:586
msgid "Town or village can not be empty."
msgstr "No podeu deixar la població en blanc."
#: pkg/customer/admin.go:355 pkg/company/admin.go:233 pkg/booking/public.go:587
#: pkg/customer/admin.go:339 pkg/company/admin.go:233 pkg/booking/public.go:587
msgid "Postcode can not be empty."
msgstr "No podeu deixar el codi postal en blanc."
#: pkg/customer/admin.go:356 pkg/company/admin.go:234 pkg/booking/admin.go:474
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:592
#: pkg/booking/public.go:588
msgid "This postcode is not valid."
msgstr "Aquest codi postal no és vàlid."
#: pkg/customer/admin.go:364 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:484
#: pkg/customer/admin.go:348 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:602
#: pkg/booking/public.go:596
msgid "This phone number is not valid."
msgstr "Aquest número de telèfon no és vàlid."
#: pkg/invoice/admin.go:581
#: pkg/invoice/admin.go:679
msgctxt "filename"
msgid "invoices.zip"
msgstr "factures.zip"
#: pkg/invoice/admin.go:596
#: pkg/invoice/admin.go:694
msgctxt "filename"
msgid "invoices.ods"
msgstr "factures.ods"
#: pkg/invoice/admin.go:598 pkg/invoice/admin.go:1260 pkg/invoice/admin.go:1267
#: pkg/invoice/admin.go:696 pkg/invoice/admin.go:1358 pkg/invoice/admin.go:1365
msgid "Invalid action"
msgstr "Acció invàlida"
#: pkg/invoice/admin.go:763
#: pkg/invoice/admin.go:861
msgid "Selected invoice status is not valid."
msgstr "Lestat de factura escollit no és vàlid."
#: pkg/invoice/admin.go:764
#: pkg/invoice/admin.go:862
msgid "Invoice date can not be empty."
msgstr "No podeu deixar la data de factura en blanc."
#: pkg/invoice/admin.go:765
#: pkg/invoice/admin.go:863
msgid "Invoice date must be a valid date."
msgstr "La data de factura ha de ser una data vàlida."
#: pkg/invoice/admin.go:923
#: pkg/invoice/admin.go:1021
#, c-format
msgid "Re: booking #%s of %s%s"
msgstr "Ref: reserva núm. %s del %s-%s"
#: pkg/invoice/admin.go:924
#: pkg/invoice/admin.go:1022
msgctxt "to_char"
msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY"
#: pkg/invoice/admin.go:1051
#: pkg/invoice/admin.go:1149
msgid "Invoice product ID must be an integer."
msgstr "LID de producte de factura ha de ser enter."
#: pkg/invoice/admin.go:1052
#: pkg/invoice/admin.go:1150
msgid "Invoice product ID one or greater."
msgstr "LID de producte de factura ha de ser com a mínim u."
#: pkg/invoice/admin.go:1056
#: pkg/invoice/admin.go:1154
msgid "Product ID must be an integer."
msgstr "LID de producte ha de ser un número enter."
#: pkg/invoice/admin.go:1057
#: pkg/invoice/admin.go:1155
msgid "Product ID must zero or greater."
msgstr "LID de producte ha de ser com a mínim zero."
#: pkg/invoice/admin.go:1066
#: pkg/invoice/admin.go:1164
msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc."
#: pkg/invoice/admin.go:1067
#: pkg/invoice/admin.go:1165
msgid "Quantity must be an integer."
msgstr "La quantitat ha de ser un número enter."
#: pkg/invoice/admin.go:1068
#: pkg/invoice/admin.go:1166
msgid "Quantity must one or greater."
msgstr "La quantitat ha de ser com a mínim u."
#: pkg/invoice/admin.go:1071
#: pkg/invoice/admin.go:1169
msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc."
#: pkg/invoice/admin.go:1072
#: pkg/invoice/admin.go:1170
msgid "Discount must be an integer."
msgstr "El descompte ha de ser un número enter."
#: pkg/invoice/admin.go:1073 pkg/invoice/admin.go:1074
#: pkg/invoice/admin.go:1171 pkg/invoice/admin.go:1172
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100"
#: pkg/invoice/admin.go:1078
#: pkg/invoice/admin.go:1176
msgid "Selected tax is not valid."
msgstr "Limpost escollit no és vàlid."
@ -3521,19 +3501,19 @@ msgctxt "filename"
msgid "bookings.ods"
msgstr "reserves.ods"
#: pkg/booking/admin.go:473
#: pkg/booking/admin.go:591
msgid "Country can not be empty to validate the postcode."
msgstr "No podeu deixar el país en blanc per validar el codi postal."
#: pkg/booking/admin.go:483
#: pkg/booking/admin.go:601
msgid "Country can not be empty to validate the phone."
msgstr "No podeu deixar el país en blanc per validar el telèfon."
#: pkg/booking/admin.go:490
#: pkg/booking/admin.go:608
msgid "You must select at least one accommodation."
msgstr "Heu descollir com a mínim un allotjament."
#: pkg/booking/admin.go:496
#: pkg/booking/admin.go:614
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Els allotjaments escollits no estan disponibles a les dates demanades."
@ -3650,9 +3630,6 @@ msgstr "El valor de %s ha de ser com a màxim %d."
msgid "It is mandatory to agree to the reservation conditions."
msgstr "És obligatori acceptar les condicions de reserves."
#~ msgid "Total"
#~ msgstr "Total"
#~ msgid "Select a customer"
#~ msgstr "Esculliu un client"

275
po/es.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-05-13 10:37+0200\n"
"POT-Creation-Date: 2024-05-03 17:19+0200\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -78,7 +78,7 @@ msgid "Payment"
msgstr "Pago"
#: web/templates/mail/payment/details.gotxt:9
#: web/templates/admin/payment/index.gohtml:73
#: web/templates/admin/payment/index.gohtml:21
#: web/templates/admin/payment/details.gohtml:52
#: web/templates/admin/prebooking/index.gohtml:59
#: web/templates/admin/booking/index.gohtml:73
@ -87,7 +87,7 @@ msgid "Reference"
msgstr "Referencia"
#: web/templates/mail/payment/details.gotxt:10
#: web/templates/admin/payment/index.gohtml:74
#: web/templates/admin/payment/index.gohtml:22
#: web/templates/admin/payment/details.gohtml:56
#: web/templates/admin/booking/index.gohtml:77
msgctxt "header"
@ -131,14 +131,14 @@ msgstr "Preferencias de área"
#: web/templates/mail/payment/details.gotxt:18
#: web/templates/admin/payment/details.gohtml:82
msgctxt "input"
msgid "ACSI / ANWB card?"
msgstr "¿Tarjeta ACSI / ANWB?"
msgid "ACSI card?"
msgstr "¿Tarjeta ACSI?"
#: web/templates/mail/payment/details.gotxt:18
#: web/templates/admin/payment/details.gohtml:83
#: web/templates/admin/campsite/type/index.gohtml:53
#: web/templates/admin/season/index.gohtml:44
#: web/templates/admin/user/results.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:31
#: web/templates/admin/amenity/index.gohtml:40
msgid "Yes"
msgstr "Sí"
@ -147,7 +147,7 @@ msgstr "Sí"
#: web/templates/admin/payment/details.gohtml:83
#: web/templates/admin/campsite/type/index.gohtml:53
#: web/templates/admin/season/index.gohtml:44
#: web/templates/admin/user/results.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:31
#: web/templates/admin/amenity/index.gohtml:40
msgid "No"
msgstr "No"
@ -179,7 +179,7 @@ msgstr "Noches"
#: web/templates/mail/payment/details.gotxt:22
#: web/templates/public/booking/fields.gohtml:60
#: web/templates/admin/payment/details.gohtml:98
#: web/templates/admin/booking/fields.gohtml:93 pkg/invoice/admin.go:964
#: web/templates/admin/booking/fields.gohtml:93 pkg/invoice/admin.go:1062
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultos de 17 años o más"
@ -187,7 +187,7 @@ msgstr "Adultos de 17 años o más"
#: web/templates/mail/payment/details.gotxt:23
#: web/templates/public/booking/fields.gohtml:71
#: web/templates/admin/payment/details.gohtml:102
#: web/templates/admin/booking/fields.gohtml:109 pkg/invoice/admin.go:965
#: web/templates/admin/booking/fields.gohtml:109 pkg/invoice/admin.go:1063
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescentes de 11 a 16 años"
@ -195,7 +195,7 @@ msgstr "Adolescentes de 11 a 16 años"
#: web/templates/mail/payment/details.gotxt:24
#: web/templates/public/booking/fields.gohtml:82
#: web/templates/admin/payment/details.gohtml:106
#: web/templates/admin/booking/fields.gohtml:125 pkg/invoice/admin.go:966
#: web/templates/admin/booking/fields.gohtml:125 pkg/invoice/admin.go:1064
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Niños de 2 a 10 años"
@ -203,14 +203,14 @@ msgstr "Niños de 2 a 10 años"
#: web/templates/mail/payment/details.gotxt:25
#: web/templates/public/booking/fields.gohtml:100
#: web/templates/admin/payment/details.gohtml:110
#: web/templates/admin/booking/fields.gohtml:140 pkg/invoice/admin.go:967
#: web/templates/admin/booking/fields.gohtml:140 pkg/invoice/admin.go:1065
msgctxt "input"
msgid "Dogs"
msgstr "Perros"
#: web/templates/mail/payment/details.gotxt:26
#: web/templates/admin/payment/details.gohtml:114
#: web/templates/admin/booking/fields.gohtml:167 pkg/invoice/admin.go:968
#: web/templates/admin/booking/fields.gohtml:167 pkg/invoice/admin.go:1066
#: pkg/booking/cart.go:242
msgctxt "cart"
msgid "Tourist tax"
@ -293,7 +293,6 @@ msgstr "País"
#: web/templates/mail/payment/details.gotxt:46
#: web/templates/public/booking/fields.gohtml:201
#: web/templates/admin/payment/details.gohtml:163
#: web/templates/admin/customer/index.gohtml:33
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
@ -419,7 +418,7 @@ msgid "Order Number"
msgstr "Número de pedido"
#: web/templates/public/payment/details.gohtml:8
#: web/templates/admin/invoice/index.gohtml:104
#: web/templates/admin/invoice/index.gohtml:103
#: web/templates/admin/invoice/view.gohtml:26
msgctxt "title"
msgid "Date"
@ -597,12 +596,6 @@ msgctxt "action"
msgid "Filters"
msgstr "Filtros"
#: web/templates/public/form.gohtml:93 web/templates/admin/form.gohtml:93
#: web/templates/admin/prebooking/results.gohtml:20
msgctxt "action"
msgid "Load more"
msgstr "Cargar más"
#: web/templates/public/campsite/type.gohtml:49
#: web/templates/public/booking/fields.gohtml:278
msgctxt "action"
@ -1043,8 +1036,8 @@ msgstr "Escoja un país"
#: web/templates/public/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:259
msgctxt "input"
msgid "ACSI / ANWB card? (optional)"
msgstr "¿Tarjeta ACSI / ANWB? (opcional)"
msgid "ACSI card? (optional)"
msgstr "¿Tarjeta ACSI? (opcional)"
#: web/templates/public/booking/fields.gohtml:255
msgctxt "input"
@ -1114,69 +1107,25 @@ msgctxt "action"
msgid "Save changes"
msgstr "Guardar los cambios"
#: web/templates/admin/payment/index.gohtml:24
msgctxt "input"
msgid "Payment status"
msgstr "Estado del pago"
#: web/templates/admin/payment/index.gohtml:28
#: web/templates/admin/invoice/index.gohtml:55
#: web/templates/admin/booking/index.gohtml:38
msgid "All statuses"
msgstr "Todos los estados"
#: web/templates/admin/payment/index.gohtml:36
#: web/templates/admin/invoice/index.gohtml:63
#: web/templates/admin/prebooking/index.gohtml:32
#: web/templates/admin/user/login-attempts.gohtml:24
#: web/templates/admin/booking/index.gohtml:46
msgctxt "input"
msgid "From date"
msgstr "De la fecha"
#: web/templates/admin/payment/index.gohtml:45
#: web/templates/admin/invoice/index.gohtml:72
#: web/templates/admin/prebooking/index.gohtml:41
#: web/templates/admin/user/login-attempts.gohtml:33
#: web/templates/admin/booking/index.gohtml:55
msgctxt "input"
msgid "To date"
msgstr "A la fecha"
#: web/templates/admin/payment/index.gohtml:54
msgctxt "input"
msgid "Reference"
msgstr "Referencia"
#: web/templates/admin/payment/index.gohtml:64
#: web/templates/admin/customer/index.gohtml:43
#: web/templates/admin/invoice/index.gohtml:94
#: web/templates/admin/prebooking/index.gohtml:51
#: web/templates/admin/user/login-attempts.gohtml:43
#: web/templates/admin/booking/index.gohtml:65
msgctxt "action"
msgid "Reset"
msgstr "Restablecer"
#: web/templates/admin/payment/index.gohtml:72
#: web/templates/admin/user/login-attempts.gohtml:51
#: web/templates/admin/payment/index.gohtml:20
#: web/templates/admin/user/login-attempts.gohtml:19
msgctxt "header"
msgid "Date"
msgstr "Fecha"
#: web/templates/admin/payment/index.gohtml:75
#: web/templates/admin/payment/index.gohtml:23
msgctxt "header"
msgid "Down payment"
msgstr "A cuenta"
#: web/templates/admin/payment/index.gohtml:76
#: web/templates/admin/payment/index.gohtml:24
#: web/templates/admin/booking/fields.gohtml:75
#: web/templates/admin/booking/fields.gohtml:173
msgctxt "header"
msgid "Total"
msgstr "Total"
#: web/templates/admin/payment/index.gohtml:84
#: web/templates/admin/payment/index.gohtml:40
msgid "No payments found."
msgstr "No se ha encontrado ningún pago."
@ -1234,7 +1183,6 @@ msgstr "Álias"
#: web/templates/admin/campsite/type/form.gohtml:51
#: web/templates/admin/campsite/type/option/form.gohtml:41
#: web/templates/admin/season/form.gohtml:50
#: web/templates/admin/customer/index.gohtml:24
#: web/templates/admin/invoice/product-form.gohtml:16
#: web/templates/admin/services/form.gohtml:53
#: web/templates/admin/profile.gohtml:29
@ -1307,7 +1255,7 @@ msgstr "Añadir texto legal"
#: web/templates/admin/campsite/type/option/index.gohtml:30
#: web/templates/admin/campsite/type/index.gohtml:29
#: web/templates/admin/season/index.gohtml:29
#: web/templates/admin/customer/index.gohtml:51
#: web/templates/admin/customer/index.gohtml:19
#: web/templates/admin/user/index.gohtml:20
#: web/templates/admin/surroundings/index.gohtml:83
#: web/templates/admin/amenity/feature/index.gohtml:30
@ -1912,7 +1860,7 @@ msgid "New Customer"
msgstr "Nuevo cliente"
#: web/templates/admin/customer/form.gohtml:15
#: web/templates/admin/invoice/index.gohtml:106
#: web/templates/admin/invoice/index.gohtml:105
msgctxt "title"
msgid "Customer"
msgstr "Cliente"
@ -1969,19 +1917,19 @@ msgctxt "action"
msgid "Add Customer"
msgstr "Añadir cliente"
#: web/templates/admin/customer/index.gohtml:52
#: web/templates/admin/user/login-attempts.gohtml:52
#: web/templates/admin/customer/index.gohtml:20
#: web/templates/admin/user/login-attempts.gohtml:20
#: web/templates/admin/user/index.gohtml:21
msgctxt "header"
msgid "Email"
msgstr "Correo-e"
#: web/templates/admin/customer/index.gohtml:53
#: web/templates/admin/customer/index.gohtml:21
msgctxt "header"
msgid "Phone"
msgstr "Teléfono"
#: web/templates/admin/customer/index.gohtml:61
#: web/templates/admin/customer/index.gohtml:33
msgid "No customer found."
msgstr "No se ha encontrado ningún cliente."
@ -2099,6 +2047,25 @@ msgstr "Cliente"
msgid "All customers"
msgstr "Todos los clientes"
#: web/templates/admin/invoice/index.gohtml:55
#: web/templates/admin/booking/index.gohtml:38
msgid "All statuses"
msgstr "Todos los estados"
#: web/templates/admin/invoice/index.gohtml:63
#: web/templates/admin/prebooking/index.gohtml:32
#: web/templates/admin/booking/index.gohtml:46
msgctxt "input"
msgid "From date"
msgstr "De la fecha"
#: web/templates/admin/invoice/index.gohtml:72
#: web/templates/admin/prebooking/index.gohtml:41
#: web/templates/admin/booking/index.gohtml:55
msgctxt "input"
msgid "To date"
msgstr "A la fecha"
#: web/templates/admin/invoice/index.gohtml:81
msgctxt "input"
msgid "Invoice number"
@ -2109,39 +2076,60 @@ msgctxt "action"
msgid "Filter"
msgstr "Filtrar"
#: web/templates/admin/invoice/index.gohtml:94
#: web/templates/admin/prebooking/index.gohtml:51
#: web/templates/admin/booking/index.gohtml:65
msgctxt "action"
msgid "Reset"
msgstr "Restablecer"
#: web/templates/admin/invoice/index.gohtml:97
msgctxt "action"
msgid "Add invoice"
msgstr "Añadir factura"
#: web/templates/admin/invoice/index.gohtml:103
#: web/templates/admin/invoice/index.gohtml:102
msgctxt "invoice"
msgid "All"
msgstr "Todas"
#: web/templates/admin/invoice/index.gohtml:105
#: web/templates/admin/invoice/index.gohtml:104
msgctxt "title"
msgid "Invoice Num."
msgstr "Núm. de factura"
#: web/templates/admin/invoice/index.gohtml:107
#: web/templates/admin/invoice/index.gohtml:106
msgctxt "title"
msgid "Status"
msgstr "Estado"
#: web/templates/admin/invoice/index.gohtml:108
#: web/templates/admin/invoice/index.gohtml:107
msgctxt "title"
msgid "Download"
msgstr "Descarga"
#: web/templates/admin/invoice/index.gohtml:109
#: web/templates/admin/invoice/index.gohtml:108
msgctxt "title"
msgid "Amount"
msgstr "Importe"
#: web/templates/admin/invoice/index.gohtml:117
msgid "No invoices found."
msgstr "No se ha encontrado ninguna factura."
#: web/templates/admin/invoice/index.gohtml:115
msgctxt "action"
msgid "Select invoice %v"
msgstr "Seleccionar factura %v"
#: web/templates/admin/invoice/index.gohtml:144
msgctxt "action"
msgid "Download invoice %s"
msgstr "Descargar factura %s"
#: web/templates/admin/invoice/index.gohtml:154
msgid "No invoices added yet."
msgstr "No se ha añadido ninguna factura todavía."
#: web/templates/admin/invoice/index.gohtml:161
msgid "Total"
msgstr "Total"
#: web/templates/admin/invoice/view.gohtml:2
msgctxt "title"
@ -2183,16 +2171,6 @@ msgctxt "title"
msgid "Tax Base"
msgstr "Base imponible"
#: web/templates/admin/invoice/results.gohtml:3
msgctxt "action"
msgid "Select invoice %v"
msgstr "Seleccionar factura %v"
#: web/templates/admin/invoice/results.gohtml:32
msgctxt "action"
msgid "Download invoice %s"
msgstr "Descargar factura %s"
#: web/templates/admin/prebooking/index.gohtml:6
#: web/templates/admin/layout.gohtml:92
#: web/templates/admin/booking/form.gohtml:20
@ -2238,6 +2216,12 @@ msgstr "Nombre del titular"
msgid "No prebooking found."
msgstr "No se ha encontrado ninguna prereserva."
#: web/templates/admin/prebooking/results.gohtml:20
#: web/templates/admin/booking/results.gohtml:23
msgctxt "action"
msgid "Load more"
msgstr "Cargar más"
#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:18
msgctxt "title"
msgid "Login"
@ -2323,7 +2307,7 @@ msgid "Password Confirmation"
msgstr "Confirmación de la contraseña"
#: web/templates/admin/user/login-attempts.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:46
#: web/templates/admin/user/login-attempts.gohtml:15
msgctxt "title"
msgid "Login Attempts"
msgstr "Intentos de entrada"
@ -2336,20 +2320,16 @@ msgctxt "title"
msgid "Users"
msgstr "Usuarios"
#: web/templates/admin/user/login-attempts.gohtml:53
#: web/templates/admin/user/login-attempts.gohtml:21
msgctxt "header"
msgid "IP Address"
msgstr "Dirección IP"
#: web/templates/admin/user/login-attempts.gohtml:54
#: web/templates/admin/user/login-attempts.gohtml:22
msgctxt "header"
msgid "Success"
msgstr "Éxito"
#: web/templates/admin/user/login-attempts.gohtml:62
msgid "No logging attempts found."
msgstr "No se ha encontrado ningún intento de entrada."
#: web/templates/admin/user/index.gohtml:14
msgctxt "action"
msgid "Add User"
@ -2721,7 +2701,7 @@ msgctxt "header"
msgid "Decription"
msgstr "Descripción"
#: web/templates/admin/booking/fields.gohtml:81 pkg/invoice/admin.go:963
#: web/templates/admin/booking/fields.gohtml:81 pkg/invoice/admin.go:1061
#: pkg/booking/cart.go:232
msgctxt "cart"
msgid "Night"
@ -2920,7 +2900,7 @@ msgstr "Se ha recibido correctamente el pago de la reserva"
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
#: pkg/campsite/feature.go:269 pkg/season/admin.go:411
#: pkg/invoice/admin.go:1060 pkg/services/admin.go:316
#: pkg/invoice/admin.go:1158 pkg/services/admin.go:316
#: pkg/surroundings/admin.go:340 pkg/amenity/feature.go:269
#: pkg/amenity/admin.go:283
msgid "Name can not be empty."
@ -2961,8 +2941,8 @@ msgstr "La imagen de la diapositiva tiene que ser un medio de tipo imagen."
msgid "Email can not be empty."
msgstr "No podéis dejar el correo-e en blanco."
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:361
#: pkg/company/admin.go:225 pkg/booking/admin.go:479 pkg/booking/public.go:593
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:345
#: pkg/company/admin.go:225 pkg/booking/admin.go:597 pkg/booking/public.go:593
msgid "This email is not valid. It should be like name@domain.com."
msgstr "Este correo-e no es válido. Tiene que ser parecido a nombre@dominio.com."
@ -3019,15 +2999,15 @@ msgstr "El valor del máximo tiene que ser un número entero."
msgid "Maximum must be equal or greater than minimum."
msgstr "El valor del máximo tiene que ser igual o mayor al del mínimo."
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1061
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1159
msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco."
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1062
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1160
msgid "Price must be a decimal number."
msgstr "El precio tiene que ser un número decimal."
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1063
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1161
msgid "Price must be zero or greater."
msgstr "El precio tiene que ser como mínimo cero."
@ -3173,7 +3153,7 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Niños (de 2 a 10 años)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:455 pkg/booking/public.go:177
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:573 pkg/booking/public.go:177
#: pkg/booking/public.go:232
msgid "Selected campsite type is not valid."
msgstr "El tipo de alojamiento escogido no es válido."
@ -3211,129 +3191,129 @@ msgstr "No podéis dejar la fecha final en blanco."
msgid "End date must be a valid date."
msgstr "La fecha final tiene que ser una fecha válida."
#: pkg/customer/admin.go:342 pkg/company/admin.go:207
#: pkg/customer/admin.go:326 pkg/company/admin.go:207
#: pkg/booking/checkin.go:300 pkg/booking/public.go:577
msgid "Selected country is not valid."
msgstr "El país escogido no es válido."
#: pkg/customer/admin.go:346 pkg/booking/checkin.go:284
#: pkg/customer/admin.go:330 pkg/booking/checkin.go:284
msgid "Selected ID document type is not valid."
msgstr "El tipo de documento de identidad escogido no es válido."
#: pkg/customer/admin.go:347 pkg/booking/checkin.go:285
#: pkg/customer/admin.go:331 pkg/booking/checkin.go:285
msgid "ID document number can not be empty."
msgstr "No podéis dejar el número del documento de identidad en blanco."
#: pkg/customer/admin.go:349 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:467
#: pkg/customer/admin.go:333 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:585
#: pkg/booking/public.go:581
msgid "Full name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco."
#: pkg/customer/admin.go:350 pkg/booking/admin.go:468 pkg/booking/public.go:582
#: pkg/customer/admin.go:334 pkg/booking/admin.go:586 pkg/booking/public.go:582
msgid "Full name must have at least one letter."
msgstr "El nombre y los apellidos tienen que tener como mínimo una letra."
#: pkg/customer/admin.go:353 pkg/company/admin.go:230 pkg/booking/public.go:585
#: pkg/customer/admin.go:337 pkg/company/admin.go:230 pkg/booking/public.go:585
msgid "Address can not be empty."
msgstr "No podéis dejar la dirección en blanco."
#: pkg/customer/admin.go:354 pkg/booking/public.go:586
#: pkg/customer/admin.go:338 pkg/booking/public.go:586
msgid "Town or village can not be empty."
msgstr "No podéis dejar la población en blanco."
#: pkg/customer/admin.go:355 pkg/company/admin.go:233 pkg/booking/public.go:587
#: pkg/customer/admin.go:339 pkg/company/admin.go:233 pkg/booking/public.go:587
msgid "Postcode can not be empty."
msgstr "No podéis dejar el código postal en blanco."
#: pkg/customer/admin.go:356 pkg/company/admin.go:234 pkg/booking/admin.go:474
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:592
#: pkg/booking/public.go:588
msgid "This postcode is not valid."
msgstr "Este código postal no es válido."
#: pkg/customer/admin.go:364 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:484
#: pkg/customer/admin.go:348 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:602
#: pkg/booking/public.go:596
msgid "This phone number is not valid."
msgstr "Este teléfono no es válido."
#: pkg/invoice/admin.go:581
#: pkg/invoice/admin.go:679
msgctxt "filename"
msgid "invoices.zip"
msgstr "facturas.zip"
#: pkg/invoice/admin.go:596
#: pkg/invoice/admin.go:694
msgctxt "filename"
msgid "invoices.ods"
msgstr "facturas.ods"
#: pkg/invoice/admin.go:598 pkg/invoice/admin.go:1260 pkg/invoice/admin.go:1267
#: pkg/invoice/admin.go:696 pkg/invoice/admin.go:1358 pkg/invoice/admin.go:1365
msgid "Invalid action"
msgstr "Acción inválida"
#: pkg/invoice/admin.go:763
#: pkg/invoice/admin.go:861
msgid "Selected invoice status is not valid."
msgstr "El estado de factura escogida no es válido."
#: pkg/invoice/admin.go:764
#: pkg/invoice/admin.go:862
msgid "Invoice date can not be empty."
msgstr "No podéis dejar la fecha de factura en blanco."
#: pkg/invoice/admin.go:765
#: pkg/invoice/admin.go:863
msgid "Invoice date must be a valid date."
msgstr "La fecha de factura tiene que ser una fecha válida."
#: pkg/invoice/admin.go:923
#: pkg/invoice/admin.go:1021
#, c-format
msgid "Re: booking #%s of %s%s"
msgstr "Ref.: reserva núm. %s del %s%s"
#: pkg/invoice/admin.go:924
#: pkg/invoice/admin.go:1022
msgctxt "to_char"
msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY"
#: pkg/invoice/admin.go:1051
#: pkg/invoice/admin.go:1149
msgid "Invoice product ID must be an integer."
msgstr "El ID de producto de factura tiene que ser entero."
#: pkg/invoice/admin.go:1052
#: pkg/invoice/admin.go:1150
msgid "Invoice product ID one or greater."
msgstr "El ID de producto de factura tiene que ser como mínimo uno."
#: pkg/invoice/admin.go:1056
#: pkg/invoice/admin.go:1154
msgid "Product ID must be an integer."
msgstr "El ID de producto tiene que ser un número entero."
#: pkg/invoice/admin.go:1057
#: pkg/invoice/admin.go:1155
msgid "Product ID must zero or greater."
msgstr "El ID de producto tiene que ser como mínimo cero."
#: pkg/invoice/admin.go:1066
#: pkg/invoice/admin.go:1164
msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco."
#: pkg/invoice/admin.go:1067
#: pkg/invoice/admin.go:1165
msgid "Quantity must be an integer."
msgstr "La cantidad tiene que ser un número entero."
#: pkg/invoice/admin.go:1068
#: pkg/invoice/admin.go:1166
msgid "Quantity must one or greater."
msgstr "La cantidad tiene que ser como mínimo uno."
#: pkg/invoice/admin.go:1071
#: pkg/invoice/admin.go:1169
msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco."
#: pkg/invoice/admin.go:1072
#: pkg/invoice/admin.go:1170
msgid "Discount must be an integer."
msgstr "El descuento tiene que ser un número entero."
#: pkg/invoice/admin.go:1073 pkg/invoice/admin.go:1074
#: pkg/invoice/admin.go:1171 pkg/invoice/admin.go:1172
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 1 y 100."
#: pkg/invoice/admin.go:1078
#: pkg/invoice/admin.go:1176
msgid "Selected tax is not valid."
msgstr "El impuesto escogido no es válido."
@ -3521,19 +3501,19 @@ msgctxt "filename"
msgid "bookings.ods"
msgstr "reservas.ods"
#: pkg/booking/admin.go:473
#: pkg/booking/admin.go:591
msgid "Country can not be empty to validate the postcode."
msgstr "No podéis dejar el país en blanco para validar el código postal."
#: pkg/booking/admin.go:483
#: pkg/booking/admin.go:601
msgid "Country can not be empty to validate the phone."
msgstr "No podéis dejar el país en blanco para validar el teléfono."
#: pkg/booking/admin.go:490
#: pkg/booking/admin.go:608
msgid "You must select at least one accommodation."
msgstr "Tenéis que seleccionar como mínimo un alojamiento."
#: pkg/booking/admin.go:496
#: pkg/booking/admin.go:614
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Los alojamientos seleccionados no tienen disponibilidad en las fechas pedidas."
@ -3650,9 +3630,6 @@ msgstr "%s tiene que ser como máximo %d"
msgid "It is mandatory to agree to the reservation conditions."
msgstr "Es obligatorio aceptar las condiciones de reserva."
#~ msgid "Total"
#~ msgstr "Total"
#~ msgid "Select a customer"
#~ msgstr "Escoja un cliente"

277
po/fr.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-05-13 10:37+0200\n"
"POT-Creation-Date: 2024-05-03 17:19+0200\n"
"PO-Revision-Date: 2024-02-06 10:05+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n"
@ -78,7 +78,7 @@ msgid "Payment"
msgstr "Paiement"
#: web/templates/mail/payment/details.gotxt:9
#: web/templates/admin/payment/index.gohtml:73
#: web/templates/admin/payment/index.gohtml:21
#: web/templates/admin/payment/details.gohtml:52
#: web/templates/admin/prebooking/index.gohtml:59
#: web/templates/admin/booking/index.gohtml:73
@ -87,7 +87,7 @@ msgid "Reference"
msgstr "Référence"
#: web/templates/mail/payment/details.gotxt:10
#: web/templates/admin/payment/index.gohtml:74
#: web/templates/admin/payment/index.gohtml:22
#: web/templates/admin/payment/details.gohtml:56
#: web/templates/admin/booking/index.gohtml:77
msgctxt "header"
@ -131,14 +131,14 @@ msgstr "Préférences de zone"
#: web/templates/mail/payment/details.gotxt:18
#: web/templates/admin/payment/details.gohtml:82
msgctxt "input"
msgid "ACSI / ANWB card?"
msgstr "Carte ACSI / ANWB ?"
msgid "ACSI card?"
msgstr "Carte ACSI ?"
#: web/templates/mail/payment/details.gotxt:18
#: web/templates/admin/payment/details.gohtml:83
#: web/templates/admin/campsite/type/index.gohtml:53
#: web/templates/admin/season/index.gohtml:44
#: web/templates/admin/user/results.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:31
#: web/templates/admin/amenity/index.gohtml:40
msgid "Yes"
msgstr "Oui"
@ -147,7 +147,7 @@ msgstr "Oui"
#: web/templates/admin/payment/details.gohtml:83
#: web/templates/admin/campsite/type/index.gohtml:53
#: web/templates/admin/season/index.gohtml:44
#: web/templates/admin/user/results.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:31
#: web/templates/admin/amenity/index.gohtml:40
msgid "No"
msgstr "Non"
@ -179,7 +179,7 @@ msgstr "Nuits"
#: web/templates/mail/payment/details.gotxt:22
#: web/templates/public/booking/fields.gohtml:60
#: web/templates/admin/payment/details.gohtml:98
#: web/templates/admin/booking/fields.gohtml:93 pkg/invoice/admin.go:964
#: web/templates/admin/booking/fields.gohtml:93 pkg/invoice/admin.go:1062
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultes âgés 17 ans ou plus"
@ -187,7 +187,7 @@ msgstr "Adultes âgés 17 ans ou plus"
#: web/templates/mail/payment/details.gotxt:23
#: web/templates/public/booking/fields.gohtml:71
#: web/templates/admin/payment/details.gohtml:102
#: web/templates/admin/booking/fields.gohtml:109 pkg/invoice/admin.go:965
#: web/templates/admin/booking/fields.gohtml:109 pkg/invoice/admin.go:1063
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents de 11 à 16 ans"
@ -195,7 +195,7 @@ msgstr "Adolescents de 11 à 16 ans"
#: web/templates/mail/payment/details.gotxt:24
#: web/templates/public/booking/fields.gohtml:82
#: web/templates/admin/payment/details.gohtml:106
#: web/templates/admin/booking/fields.gohtml:125 pkg/invoice/admin.go:966
#: web/templates/admin/booking/fields.gohtml:125 pkg/invoice/admin.go:1064
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Enfants de 2 à 10 ans"
@ -203,14 +203,14 @@ msgstr "Enfants de 2 à 10 ans"
#: web/templates/mail/payment/details.gotxt:25
#: web/templates/public/booking/fields.gohtml:100
#: web/templates/admin/payment/details.gohtml:110
#: web/templates/admin/booking/fields.gohtml:140 pkg/invoice/admin.go:967
#: web/templates/admin/booking/fields.gohtml:140 pkg/invoice/admin.go:1065
msgctxt "input"
msgid "Dogs"
msgstr "Chiens"
#: web/templates/mail/payment/details.gotxt:26
#: web/templates/admin/payment/details.gohtml:114
#: web/templates/admin/booking/fields.gohtml:167 pkg/invoice/admin.go:968
#: web/templates/admin/booking/fields.gohtml:167 pkg/invoice/admin.go:1066
#: pkg/booking/cart.go:242
msgctxt "cart"
msgid "Tourist tax"
@ -293,7 +293,6 @@ msgstr "Pays"
#: web/templates/mail/payment/details.gotxt:46
#: web/templates/public/booking/fields.gohtml:201
#: web/templates/admin/payment/details.gohtml:163
#: web/templates/admin/customer/index.gohtml:33
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
@ -419,7 +418,7 @@ msgid "Order Number"
msgstr "Numéro de commande"
#: web/templates/public/payment/details.gohtml:8
#: web/templates/admin/invoice/index.gohtml:104
#: web/templates/admin/invoice/index.gohtml:103
#: web/templates/admin/invoice/view.gohtml:26
msgctxt "title"
msgid "Date"
@ -597,12 +596,6 @@ msgctxt "action"
msgid "Filters"
msgstr "Filtres"
#: web/templates/public/form.gohtml:93 web/templates/admin/form.gohtml:93
#: web/templates/admin/prebooking/results.gohtml:20
msgctxt "action"
msgid "Load more"
msgstr "Charger plus"
#: web/templates/public/campsite/type.gohtml:49
#: web/templates/public/booking/fields.gohtml:278
msgctxt "action"
@ -1043,8 +1036,8 @@ msgstr "Choisissez un pays"
#: web/templates/public/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:259
msgctxt "input"
msgid "ACSI / ANWB card? (optional)"
msgstr "Carte ACSI / ANWB ? (facultatif)"
msgid "ACSI card? (optional)"
msgstr "Carte ACSI ? (facultatif)"
#: web/templates/public/booking/fields.gohtml:255
msgctxt "input"
@ -1053,7 +1046,7 @@ msgstr "Jai lu et jaccepte %[1]sles conditions de réservation%[2]s"
#: web/templates/public/booking/fields.gohtml:263
msgid "By down paying the %d %% of the total, you are pre-booking your preferences. We will respond within 24 hours and this percentage will be charged if accepted."
msgstr "En effectuant le paiement de %d %% du total vous pré-réservez vos préférences. Nous vous répondrons dans les 24 heures et ce pourcentage sera facturé en cas dacceptation."
msgstr "En En effectuant le paiement de %d %% du total vous pré-réservez vos préférences. Nous vous répondrons dans les 24 heures et ce pourcentage sera facturé en cas dacceptation."
#: web/templates/public/booking/fields.gohtml:265
msgid "By paying the total you are pre-booking your preferences. We will respond within 24 hours and this amount will be charged if accepted."
@ -1114,69 +1107,25 @@ msgctxt "action"
msgid "Save changes"
msgstr "Enregistrer les changements"
#: web/templates/admin/payment/index.gohtml:24
msgctxt "input"
msgid "Payment status"
msgstr "Statut du paiement"
#: web/templates/admin/payment/index.gohtml:28
#: web/templates/admin/invoice/index.gohtml:55
#: web/templates/admin/booking/index.gohtml:38
msgid "All statuses"
msgstr "Tous les statuts"
#: web/templates/admin/payment/index.gohtml:36
#: web/templates/admin/invoice/index.gohtml:63
#: web/templates/admin/prebooking/index.gohtml:32
#: web/templates/admin/user/login-attempts.gohtml:24
#: web/templates/admin/booking/index.gohtml:46
msgctxt "input"
msgid "From date"
msgstr "Partir de la date"
#: web/templates/admin/payment/index.gohtml:45
#: web/templates/admin/invoice/index.gohtml:72
#: web/templates/admin/prebooking/index.gohtml:41
#: web/templates/admin/user/login-attempts.gohtml:33
#: web/templates/admin/booking/index.gohtml:55
msgctxt "input"
msgid "To date"
msgstr "À ce jour"
#: web/templates/admin/payment/index.gohtml:54
msgctxt "input"
msgid "Reference"
msgstr "Référence"
#: web/templates/admin/payment/index.gohtml:64
#: web/templates/admin/customer/index.gohtml:43
#: web/templates/admin/invoice/index.gohtml:94
#: web/templates/admin/prebooking/index.gohtml:51
#: web/templates/admin/user/login-attempts.gohtml:43
#: web/templates/admin/booking/index.gohtml:65
msgctxt "action"
msgid "Reset"
msgstr "Réinitialiser"
#: web/templates/admin/payment/index.gohtml:72
#: web/templates/admin/user/login-attempts.gohtml:51
#: web/templates/admin/payment/index.gohtml:20
#: web/templates/admin/user/login-attempts.gohtml:19
msgctxt "header"
msgid "Date"
msgstr "Date"
#: web/templates/admin/payment/index.gohtml:75
#: web/templates/admin/payment/index.gohtml:23
msgctxt "header"
msgid "Down payment"
msgstr "Acompte"
#: web/templates/admin/payment/index.gohtml:76
#: web/templates/admin/payment/index.gohtml:24
#: web/templates/admin/booking/fields.gohtml:75
#: web/templates/admin/booking/fields.gohtml:173
msgctxt "header"
msgid "Total"
msgstr "Totale"
#: web/templates/admin/payment/index.gohtml:84
#: web/templates/admin/payment/index.gohtml:40
msgid "No payments found."
msgstr "Aucun paiement trouvée."
@ -1234,7 +1183,6 @@ msgstr "Slug"
#: web/templates/admin/campsite/type/form.gohtml:51
#: web/templates/admin/campsite/type/option/form.gohtml:41
#: web/templates/admin/season/form.gohtml:50
#: web/templates/admin/customer/index.gohtml:24
#: web/templates/admin/invoice/product-form.gohtml:16
#: web/templates/admin/services/form.gohtml:53
#: web/templates/admin/profile.gohtml:29
@ -1307,7 +1255,7 @@ msgstr "Ajouter un texte juridique"
#: web/templates/admin/campsite/type/option/index.gohtml:30
#: web/templates/admin/campsite/type/index.gohtml:29
#: web/templates/admin/season/index.gohtml:29
#: web/templates/admin/customer/index.gohtml:51
#: web/templates/admin/customer/index.gohtml:19
#: web/templates/admin/user/index.gohtml:20
#: web/templates/admin/surroundings/index.gohtml:83
#: web/templates/admin/amenity/feature/index.gohtml:30
@ -1912,7 +1860,7 @@ msgid "New Customer"
msgstr "Nouveau client"
#: web/templates/admin/customer/form.gohtml:15
#: web/templates/admin/invoice/index.gohtml:106
#: web/templates/admin/invoice/index.gohtml:105
msgctxt "title"
msgid "Customer"
msgstr "Client"
@ -1969,19 +1917,19 @@ msgctxt "action"
msgid "Add Customer"
msgstr "Ajouter un client"
#: web/templates/admin/customer/index.gohtml:52
#: web/templates/admin/user/login-attempts.gohtml:52
#: web/templates/admin/customer/index.gohtml:20
#: web/templates/admin/user/login-attempts.gohtml:20
#: web/templates/admin/user/index.gohtml:21
msgctxt "header"
msgid "Email"
msgstr "E-mail"
#: web/templates/admin/customer/index.gohtml:53
#: web/templates/admin/customer/index.gohtml:21
msgctxt "header"
msgid "Phone"
msgstr "Téléphone"
#: web/templates/admin/customer/index.gohtml:61
#: web/templates/admin/customer/index.gohtml:33
msgid "No customer found."
msgstr "Aucun client trouvée."
@ -2099,6 +2047,25 @@ msgstr "Client"
msgid "All customers"
msgstr "Tous les clients"
#: web/templates/admin/invoice/index.gohtml:55
#: web/templates/admin/booking/index.gohtml:38
msgid "All statuses"
msgstr "Tous les statuts"
#: web/templates/admin/invoice/index.gohtml:63
#: web/templates/admin/prebooking/index.gohtml:32
#: web/templates/admin/booking/index.gohtml:46
msgctxt "input"
msgid "From date"
msgstr "Partir de la date"
#: web/templates/admin/invoice/index.gohtml:72
#: web/templates/admin/prebooking/index.gohtml:41
#: web/templates/admin/booking/index.gohtml:55
msgctxt "input"
msgid "To date"
msgstr "À ce jour"
#: web/templates/admin/invoice/index.gohtml:81
msgctxt "input"
msgid "Invoice number"
@ -2109,39 +2076,60 @@ msgctxt "action"
msgid "Filter"
msgstr "Filtrer"
#: web/templates/admin/invoice/index.gohtml:94
#: web/templates/admin/prebooking/index.gohtml:51
#: web/templates/admin/booking/index.gohtml:65
msgctxt "action"
msgid "Reset"
msgstr "Réinitialiser"
#: web/templates/admin/invoice/index.gohtml:97
msgctxt "action"
msgid "Add invoice"
msgstr "Nouvelle facture"
#: web/templates/admin/invoice/index.gohtml:103
#: web/templates/admin/invoice/index.gohtml:102
msgctxt "invoice"
msgid "All"
msgstr "Toutes"
#: web/templates/admin/invoice/index.gohtml:105
#: web/templates/admin/invoice/index.gohtml:104
msgctxt "title"
msgid "Invoice Num."
msgstr "Num. de facture"
#: web/templates/admin/invoice/index.gohtml:107
#: web/templates/admin/invoice/index.gohtml:106
msgctxt "title"
msgid "Status"
msgstr "Statut"
#: web/templates/admin/invoice/index.gohtml:108
#: web/templates/admin/invoice/index.gohtml:107
msgctxt "title"
msgid "Download"
msgstr "Téléchargement"
#: web/templates/admin/invoice/index.gohtml:109
#: web/templates/admin/invoice/index.gohtml:108
msgctxt "title"
msgid "Amount"
msgstr "Import"
#: web/templates/admin/invoice/index.gohtml:117
msgid "No invoices found."
msgstr "Aucune facture trouvée."
#: web/templates/admin/invoice/index.gohtml:115
msgctxt "action"
msgid "Select invoice %v"
msgstr "Sélectionner la facture %v"
#: web/templates/admin/invoice/index.gohtml:144
msgctxt "action"
msgid "Download invoice %s"
msgstr "Télécharger la facture %s"
#: web/templates/admin/invoice/index.gohtml:154
msgid "No invoices added yet."
msgstr "Aucune facture na encore été ajouté."
#: web/templates/admin/invoice/index.gohtml:161
msgid "Total"
msgstr "Totale"
#: web/templates/admin/invoice/view.gohtml:2
msgctxt "title"
@ -2183,16 +2171,6 @@ msgctxt "title"
msgid "Tax Base"
msgstr "Import imposable"
#: web/templates/admin/invoice/results.gohtml:3
msgctxt "action"
msgid "Select invoice %v"
msgstr "Sélectionner la facture %v"
#: web/templates/admin/invoice/results.gohtml:32
msgctxt "action"
msgid "Download invoice %s"
msgstr "Télécharger la facture %s"
#: web/templates/admin/prebooking/index.gohtml:6
#: web/templates/admin/layout.gohtml:92
#: web/templates/admin/booking/form.gohtml:20
@ -2238,6 +2216,12 @@ msgstr "Nom du titulaire"
msgid "No prebooking found."
msgstr "Aucune pré-réservation trouvée."
#: web/templates/admin/prebooking/results.gohtml:20
#: web/templates/admin/booking/results.gohtml:23
msgctxt "action"
msgid "Load more"
msgstr "Charger plus"
#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:18
msgctxt "title"
msgid "Login"
@ -2323,7 +2307,7 @@ msgid "Password Confirmation"
msgstr "Confirmation du mot de passe"
#: web/templates/admin/user/login-attempts.gohtml:6
#: web/templates/admin/user/login-attempts.gohtml:46
#: web/templates/admin/user/login-attempts.gohtml:15
msgctxt "title"
msgid "Login Attempts"
msgstr "Tentatives de connexion"
@ -2336,20 +2320,16 @@ msgctxt "title"
msgid "Users"
msgstr "Utilisateurs"
#: web/templates/admin/user/login-attempts.gohtml:53
#: web/templates/admin/user/login-attempts.gohtml:21
msgctxt "header"
msgid "IP Address"
msgstr "Adresse IP"
#: web/templates/admin/user/login-attempts.gohtml:54
#: web/templates/admin/user/login-attempts.gohtml:22
msgctxt "header"
msgid "Success"
msgstr "Succès"
#: web/templates/admin/user/login-attempts.gohtml:62
msgid "No logging attempts found."
msgstr "Aucune tentative de journalisation trouvée."
#: web/templates/admin/user/index.gohtml:14
msgctxt "action"
msgid "Add User"
@ -2721,7 +2701,7 @@ msgctxt "header"
msgid "Decription"
msgstr "Description"
#: web/templates/admin/booking/fields.gohtml:81 pkg/invoice/admin.go:963
#: web/templates/admin/booking/fields.gohtml:81 pkg/invoice/admin.go:1061
#: pkg/booking/cart.go:232
msgctxt "cart"
msgid "Night"
@ -2920,7 +2900,7 @@ msgstr "Paiement de réservation reçu avec succès"
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
#: pkg/campsite/feature.go:269 pkg/season/admin.go:411
#: pkg/invoice/admin.go:1060 pkg/services/admin.go:316
#: pkg/invoice/admin.go:1158 pkg/services/admin.go:316
#: pkg/surroundings/admin.go:340 pkg/amenity/feature.go:269
#: pkg/amenity/admin.go:283
msgid "Name can not be empty."
@ -2961,8 +2941,8 @@ msgstr "Limage de la diapositive doit être de type média dimage."
msgid "Email can not be empty."
msgstr "Le-mail ne peut pas être vide."
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:361
#: pkg/company/admin.go:225 pkg/booking/admin.go:479 pkg/booking/public.go:593
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:345
#: pkg/company/admin.go:225 pkg/booking/admin.go:597 pkg/booking/public.go:593
msgid "This email is not valid. It should be like name@domain.com."
msgstr "Cette adresse e-mail nest pas valide. Il devrait en être name@domain.com."
@ -3019,15 +2999,15 @@ msgstr "Le maximum doit être un nombre entier."
msgid "Maximum must be equal or greater than minimum."
msgstr "Le maximum doit être égal ou supérieur au minimum."
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1061
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1159
msgid "Price can not be empty."
msgstr "Le prix ne peut pas être vide."
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1062
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1160
msgid "Price must be a decimal number."
msgstr "Le prix doit être un nombre décimal."
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1063
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1161
msgid "Price must be zero or greater."
msgstr "Le prix doit être égal ou supérieur à zéro."
@ -3173,7 +3153,7 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Enfants (de 2 à 10 anys)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:455 pkg/booking/public.go:177
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:573 pkg/booking/public.go:177
#: pkg/booking/public.go:232
msgid "Selected campsite type is not valid."
msgstr "Le type demplacement sélectionné nest pas valide."
@ -3211,129 +3191,129 @@ msgstr "La date de fin ne peut pas être vide."
msgid "End date must be a valid date."
msgstr "La date de fin doit être une date valide."
#: pkg/customer/admin.go:342 pkg/company/admin.go:207
#: pkg/customer/admin.go:326 pkg/company/admin.go:207
#: pkg/booking/checkin.go:300 pkg/booking/public.go:577
msgid "Selected country is not valid."
msgstr "Le pays sélectionné nest pas valide."
#: pkg/customer/admin.go:346 pkg/booking/checkin.go:284
#: pkg/customer/admin.go:330 pkg/booking/checkin.go:284
msgid "Selected ID document type is not valid."
msgstr "Le type de document didentité sélectionné nest pas valide."
#: pkg/customer/admin.go:347 pkg/booking/checkin.go:285
#: pkg/customer/admin.go:331 pkg/booking/checkin.go:285
msgid "ID document number can not be empty."
msgstr "Le numéro de documento didentité ne peut pas être vide."
#: pkg/customer/admin.go:349 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:467
#: pkg/customer/admin.go:333 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:585
#: pkg/booking/public.go:581
msgid "Full name can not be empty."
msgstr "Le nom complet ne peut pas être vide."
#: pkg/customer/admin.go:350 pkg/booking/admin.go:468 pkg/booking/public.go:582
#: pkg/customer/admin.go:334 pkg/booking/admin.go:586 pkg/booking/public.go:582
msgid "Full name must have at least one letter."
msgstr "Le nom complet doit comporter au moins une lettre."
#: pkg/customer/admin.go:353 pkg/company/admin.go:230 pkg/booking/public.go:585
#: pkg/customer/admin.go:337 pkg/company/admin.go:230 pkg/booking/public.go:585
msgid "Address can not be empty."
msgstr "Ladresse ne peut pas être vide."
#: pkg/customer/admin.go:354 pkg/booking/public.go:586
#: pkg/customer/admin.go:338 pkg/booking/public.go:586
msgid "Town or village can not be empty."
msgstr "La ville ne peut pas être vide."
#: pkg/customer/admin.go:355 pkg/company/admin.go:233 pkg/booking/public.go:587
#: pkg/customer/admin.go:339 pkg/company/admin.go:233 pkg/booking/public.go:587
msgid "Postcode can not be empty."
msgstr "Le code postal ne peut pas être vide."
#: pkg/customer/admin.go:356 pkg/company/admin.go:234 pkg/booking/admin.go:474
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:592
#: pkg/booking/public.go:588
msgid "This postcode is not valid."
msgstr "Ce code postal nest pas valide."
#: pkg/customer/admin.go:364 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:484
#: pkg/customer/admin.go:348 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:602
#: pkg/booking/public.go:596
msgid "This phone number is not valid."
msgstr "Ce numéro de téléphone nest pas valide."
#: pkg/invoice/admin.go:581
#: pkg/invoice/admin.go:679
msgctxt "filename"
msgid "invoices.zip"
msgstr "factures.zip"
#: pkg/invoice/admin.go:596
#: pkg/invoice/admin.go:694
msgctxt "filename"
msgid "invoices.ods"
msgstr "factures.ods"
#: pkg/invoice/admin.go:598 pkg/invoice/admin.go:1260 pkg/invoice/admin.go:1267
#: pkg/invoice/admin.go:696 pkg/invoice/admin.go:1358 pkg/invoice/admin.go:1365
msgid "Invalid action"
msgstr "Actin invalide"
#: pkg/invoice/admin.go:763
#: pkg/invoice/admin.go:861
msgid "Selected invoice status is not valid."
msgstr "Lstatut sélectionné nest pas valide."
#: pkg/invoice/admin.go:764
#: pkg/invoice/admin.go:862
msgid "Invoice date can not be empty."
msgstr "La date de facture ne peut pas être vide."
#: pkg/invoice/admin.go:765
#: pkg/invoice/admin.go:863
msgid "Invoice date must be a valid date."
msgstr "La date de facture doit être une date valide."
#: pkg/invoice/admin.go:923
#: pkg/invoice/admin.go:1021
#, c-format
msgid "Re: booking #%s of %s%s"
msgstr "Réf. : réservation num. %s du %s%s"
#: pkg/invoice/admin.go:924
#: pkg/invoice/admin.go:1022
msgctxt "to_char"
msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY"
#: pkg/invoice/admin.go:1051
#: pkg/invoice/admin.go:1149
msgid "Invoice product ID must be an integer."
msgstr "Le ID de produit de facture doit être un entier."
#: pkg/invoice/admin.go:1052
#: pkg/invoice/admin.go:1150
msgid "Invoice product ID one or greater."
msgstr "Le ID de produit de facture doit être égal ou supérieur à un."
#: pkg/invoice/admin.go:1056
#: pkg/invoice/admin.go:1154
msgid "Product ID must be an integer."
msgstr "Le ID de produit doit être un entier."
#: pkg/invoice/admin.go:1057
#: pkg/invoice/admin.go:1155
msgid "Product ID must zero or greater."
msgstr "Le ID de produit doit être égal ou supérieur à zéro."
#: pkg/invoice/admin.go:1066
#: pkg/invoice/admin.go:1164
msgid "Quantity can not be empty."
msgstr "La quantité ne peut pas être vide."
#: pkg/invoice/admin.go:1067
#: pkg/invoice/admin.go:1165
msgid "Quantity must be an integer."
msgstr "La quantité doit être un entier."
#: pkg/invoice/admin.go:1068
#: pkg/invoice/admin.go:1166
msgid "Quantity must one or greater."
msgstr "La quantité doit être égnal ou supérieur à zéro."
#: pkg/invoice/admin.go:1071
#: pkg/invoice/admin.go:1169
msgid "Discount can not be empty."
msgstr "Le rabais ne peut pas être vide."
#: pkg/invoice/admin.go:1072
#: pkg/invoice/admin.go:1170
msgid "Discount must be an integer."
msgstr "Le rabais doit être un entier."
#: pkg/invoice/admin.go:1073 pkg/invoice/admin.go:1074
#: pkg/invoice/admin.go:1171 pkg/invoice/admin.go:1172
msgid "Discount must be a percentage between 0 and 100."
msgstr "Le rabais doit être un pourcentage compris entre 0 et 100."
#: pkg/invoice/admin.go:1078
#: pkg/invoice/admin.go:1176
msgid "Selected tax is not valid."
msgstr "La taxe sélectionnée nest pas valide."
@ -3521,19 +3501,19 @@ msgctxt "filename"
msgid "bookings.ods"
msgstr "reservations.ods"
#: pkg/booking/admin.go:473
#: pkg/booking/admin.go:591
msgid "Country can not be empty to validate the postcode."
msgstr "Le pays ne peut pas être vide pour valider le code postal."
#: pkg/booking/admin.go:483
#: pkg/booking/admin.go:601
msgid "Country can not be empty to validate the phone."
msgstr "Le pays ne peut pas être vide pour valider le téléphone."
#: pkg/booking/admin.go:490
#: pkg/booking/admin.go:608
msgid "You must select at least one accommodation."
msgstr "Vous devez sélectionner au moins un hébergement."
#: pkg/booking/admin.go:496
#: pkg/booking/admin.go:614
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Les hébergements sélectionnés nont pas de disponibilités aux dates demandées."
@ -3650,9 +3630,6 @@ msgstr "%s doit être tout au plus %d."
msgid "It is mandatory to agree to the reservation conditions."
msgstr "Il est obligatoire daccepter les conditions de réservation."
#~ msgid "Total"
#~ msgstr "Totale"
#~ msgid "Select a customer"
#~ msgstr "Choisissez un client"

View File

@ -1,9 +0,0 @@
-- Revert camper:campsite_type__operating_dates from pg
begin;
alter table camper.campsite_type
drop column if exists operating_dates
;
commit;

View File

@ -333,4 +333,3 @@ booking_invoice [roles schema_camper booking invoice] 2024-04-28T19:45:05Z jordi
marshal_payment [roles schema_camper payment payment_customer payment_option payment__acsi_card payment_customer__-acsi_card] 2024-04-29T17:11:59Z jordi fita mas <jordi@tandem.blog> # Add function to marshal a payment
unmarshal_booking [roles schema_camper booking booking_option extension_pg_libphonenumber] 2024-04-29T17:20:38Z jordi fita mas <jordi@tandem.blog> # Add function to unmarshal a booking
cancel_booking [roles schema_camper booking booking_campsite] 2024-05-03T14:27:31Z jordi fita mas <jordi@tandem.blog> # Add function to cancel a booking
campsite_type__operating_dates [campsite_type] 2024-07-15T21:27:19Z jordi fita mas <jordi@tandem.blog> # Add operating_dates field to campsite_type

View File

@ -1,38 +0,0 @@
qt_add_executable(${PROJECT_NAME}
main.cpp
)
qt_add_qml_module(${PROJECT_NAME}
URI Camper
VERSION 1.0
DEPENDENCIES QtCore
SOURCES
database.cpp database.h
QML_FILES
ErrorNotification.qml
Main.qml
SelectableLabel.qml
)
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER ws.tandem.${PROJECT_NAME}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
Qt6::Concurrent
Qt6::Quick
Qt6::QuickControls2
Qt6::Sql
)
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME}
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

View File

@ -1,117 +0,0 @@
import QtQuick
import QtQuick.Controls
Control {
id: control
property alias text: label.text
function show(errorMessage: string) {
control.text = errorMessage;
control.visible = true;
hideTimer.start();
}
Accessible.ignored: !visible
Accessible.role: Accessible.AlertMessage
implicitHeight: visible ? (contentLayout.implicitHeight + topPadding + bottomPadding) : 0
opacity: visible ? 1 : 0
padding: 4
visible: false
background: Rectangle {
id: borderRect
border.color: "#da4453"
color: "#ebced2"
radius: 5
}
contentItem: Item {
id: contentLayout
Accessible.ignored: true
implicitHeight: Math.max(label.implicitHeight, closeButton.implicitHeight)
Behavior on opacity {
enabled: control.visible
NumberAnimation {
duration: 200
}
}
SelectableLabel {
id: label
Accessible.ignored: !control.visible
anchors {
left: parent.left
leftMargin: 4
right: closeButton.left
rightMargin: 4
top: parent.top
}
}
ToolButton {
id: closeButton
Accessible.ignored: !control.visible
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
display: ToolButton.IconOnly
height: implicitHeight
icon.name: "dialog-close"
text: qsTr("Close")
onClicked: function () {
control.visible = false;
}
}
}
Behavior on implicitHeight {
enabled: !control.visible
NumberAnimation {
duration: 200
}
}
Behavior on opacity {
enabled: !control.visible
NumberAnimation {
duration: 200
}
}
onImplicitHeightChanged: function () {
height = implicitHeight;
}
onOpacityChanged: function () {
if (opacity === 0) {
contentLayout.opacity = 0;
} else if (opacity === 1) {
contentLayout.opacity = 1;
}
}
anchors {
bottom: parent.bottom
bottomMargin: 8
left: parent.left
margins: 18 * 4
right: parent.right
}
Timer {
id: hideTimer
interval: 10000
repeat: false
onTriggered: function () {
control.visible = false;
}
}
}

View File

@ -1,80 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Camper
ApplicationWindow {
height: 480
title: qsTr("Camper")
visible: true
width: 640
ColumnLayout {
Label {
text: qsTr("&User:")
}
TextField {
id: user
focus: true
validator: RegularExpressionValidator {
regularExpression: /[^s].*/
}
onAccepted: function () {
loginAction.trigger();
}
}
Label {
text: qsTr("&Password:")
}
TextField {
id: password
echoMode: TextInput.Password
onAccepted: function () {
loginAction.trigger();
}
}
Button {
action: loginAction
}
}
ErrorNotification {
id: errorNotification
anchors {
bottom: parent.bottom
bottomMargin: 8
left: parent.left
margins: 18 * 4
right: parent.right
}
}
Action {
id: loginAction
enabled: user.acceptableInput
text: "&Login"
onTriggered: function () {
Database.open(user.text, password.text);
}
}
Connections {
function onErrorOcurred(errorMessage) {
errorNotification.show(errorMessage);
}
target: Database
}
}

View File

@ -1,21 +0,0 @@
import QtQuick
import QtQuick.Controls
Control {
id: control
property alias text: textArea.text
contentItem: TextArea {
id: textArea
padding: 0
readOnly: true
selectByMouse: true
wrapMode: Text.WordWrap
HoverHandler {
cursorShape: Qt.IBeamCursor
}
}
}

View File

@ -1,27 +0,0 @@
#include "database.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QtConcurrent>
Database::Database(QObject *parent)
: QObject{parent}
, m_pool{}
{
m_pool.setMaxThreadCount(1);
m_pool.setExpiryTimeout(-1);
}
QFuture<void> Database::open(const QString &user, const QString &password)
{
return QtConcurrent::run(&m_pool, [this, user, password]() {
QString connectionName("main");
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL", connectionName);
db.setConnectOptions("service=camper; options=-csearch_path=camper,public");
if (!db.open(user, password)) {
const QString errorMessage(db.lastError().text());
db = QSqlDatabase(); // Otherwise removeDatabase complains is still being used.
QSqlDatabase::removeDatabase(connectionName);
emit errorOcurred(errorMessage);
}
});
}

View File

@ -1,27 +0,0 @@
#ifndef DATABASE_H
#define DATABASE_H
#include <QFuture>
#include <QObject>
#include <QThreadPool>
#include <QtQmlIntegration>
class Database : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_ELEMENT
public:
explicit Database(QObject *parent = nullptr);
Q_INVOKABLE QFuture<void> open(const QString &user, const QString &password);
signals:
void errorOcurred(const QString &errorMessage);
private:
QThreadPool m_pool;
};
#endif // DATABASE_H

View File

@ -1,21 +0,0 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setApplicationName("Camper");
QCoreApplication::setOrganizationName("Tandem");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreationFailed,
&app,
[]() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.loadFromModule("Camper", "Main");
return app.exec();
}

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(113);
select plan(108);
set search_path to camper, public;
@ -118,12 +118,6 @@ select col_not_null('campsite_type', 'active');
select col_has_default('campsite_type', 'active');
select col_default_is('campsite_type', 'active', 'true');
select has_column('campsite_type', 'operating_dates');
select col_type_is('campsite_type', 'operating_dates', 'daterange');
select col_not_null('campsite_type', 'operating_dates');
select col_has_default('campsite_type', 'operating_dates');
select col_default_is('campsite_type', 'operating_dates', 'empty');
select has_column('campsite_type', 'position');
select col_type_is('campsite_type', 'position', 'integer');
select col_not_null('campsite_type', 'position');

View File

@ -1,10 +0,0 @@
-- Verify camper:campsite_type__operating_dates on pg
begin;
select operating_dates
from camper.campsite_type
where false
;
rollback;

View File

@ -256,7 +256,7 @@
<label class="colspan" data-hx-get="{{ $.URL }}" data-hx-trigger="change">
<input type="checkbox" name="{{ .Name }}" {{ if .Checked}}checked{{ end }}
{{ template "error-attrs" . }}
> {{( pgettext "ACSI / ANWB card? (optional)" "input" )}}<br>
> {{( pgettext "ACSI card? (optional)" "input" )}}<br>
{{ template "error-message" . }}
</label>
{{- end }}

View File

@ -7,4 +7,22 @@
<td class="booking-status">{{ .StatusLabel }}</td>
</tr>
{{- end }}
{{ template "pagination" .Filters.Cursor | colspan 5 }}
{{ if .Filters.Cursor.Val }}
<tr>
<td colspan="5">
{{ with .Filters -}}
<form data-hx-get="/admin/bookings" data-hx-target="closest tr" data-hx-swap="outerHTML">
{{ with .HolderName -}}<input type="hidden" name="{{ .Name }}"
value="{{ .Val }}">{{- end }}
{{ with .BookingStatus -}}<input type="hidden" name="{{ .Name }}"
value="{{ .String }}">{{- end }}
{{ with .FromDate -}}<input type="hidden" name="{{ .Name }}"
value="{{ .Val }}">{{- end }}
{{ with .ToDate -}}<input type="hidden" name="{{ .Name }}" value="{{ .Val }}">{{- end }}
{{ with .Cursor -}}<input type="hidden" name="{{ .Name }}" value="{{ .Val }}">{{- end }}
<button type="submit">{{( pgettext "Load more" "action" )}}</button>
</form>
{{- end }}
<td>
</tr>
{{- end }}

View File

@ -5,4 +5,15 @@
<td>{{ .Phone }}</td>
</tr>
{{- end }}
{{ template "pagination" .Filters.Cursor | colspan 3 }}
{{ if .Filters.Cursor.Val }}
<tr>
<td colspan="3">
{{ with .Filters -}}
<form method="get" data-hx-push-url="false" data-hx-boost="true" data-hx-target="closest tr" data-hx-swap="outerHTML">
{{ with .Cursor -}}<input type="hidden" name="{{ .Name }}" value="{{ .Val }}">{{- end }}
<button type="submit">{{( pgettext "Load more" "action" )}}</button>
</form>
{{- end }}
<td>
</tr>
{{- end }}

View File

@ -82,17 +82,3 @@
@click="document.body.classList.toggle('filters-visible')"
type="button">{{(pgettext "Filters" "action")}}</button>
{{- end }}
{{ define "pagination" -}}
{{ if .Val }}
<tr>
<td colspan="{{ .Colspan }}">
<form method="get" data-hx-push-url="false" data-hx-boost="true" data-hx-target="closest tr"
data-hx-swap="outerHTML">
<input type="hidden" name="{{ .Name }}" value="{{ .Val }}">
<button type="submit">{{( pgettext "Load more" "action" )}}</button>
</form>
<td>
</tr>
{{- end }}
{{- end }}

View File

@ -96,7 +96,6 @@
</form>
<a href="/admin/invoices/new">{{( pgettext "Add invoice" "action" )}}</a>
<h2>{{ template "title" . }}</h2>
{{ if .Invoices }}
<table id="invoice-list">
<thead>
<tr>
@ -110,10 +109,60 @@
</tr>
</thead>
<tbody>
{{ template "results.gohtml" . }}
</tbody>
</table>
{{- else -}}
<p>{{( gettext "No invoices found." )}}</p>
{{ end }}
{{ with .Invoices }}
{{- range $invoice := . }}
<tr>
{{ $title := .Number | printf (pgettext "Select invoice %v" "action") }}
<td><input type="checkbox" form="batch-form"
name="invoice" value="{{ .Slug }}"
aria-label="{{ $title }}"
title="{{ $title }}"/></td>
<td>{{ .Date|formatDate }}</td>
<td><a href="/admin/invoices/{{ .Slug }}">{{ .Number }}</a></td>
<td>{{ .CustomerName }}</td>
<td class="invoice-status-{{ .Status }}">
<details class="invoice-status menu">
<summary >{{ .StatusLabel }}</summary>
<form data-hx-put="/admin/invoices/{{ .Slug }}">
{{ CSRFInput }}
<input type="hidden" name="quick" value="status">
<ul role="menu">
{{- range $status, $name := $.InvoiceStatuses }}
{{- if ne $status $invoice.Status }}
<li role="presentation">
<button role="menuitem" type="submit"
name="invoice_status" value="{{ $status }}"
class="invoice-status-{{ $status }}"
>{{ $name }}</button>
</li>
{{- end }}
{{- end }}
</ul>
</form>
</details>
</td>
{{- $title = .Number | printf (pgettext "Download invoice %s" "action") -}}
<td class="invoice-download"><a href="/admin/invoices/{{ .Slug }}.pdf"
download="{{ .Number}}-{{ .CustomerName | slugify }}.pdf"
title="{{( pgettext "Download invoice" "action" )}}"
aria-label="{{ $title }}">⤓</a></td>
<td class="numeric">{{ .Total|formatPrice }}</td>
</tr>
{{- end }}
{{ else }}
<tr>
<td colspan="9">{{( gettext "No invoices added yet." )}}</td>
</tr>
{{ end }}
</tbody>
{{ if .Invoices }}
<tfoot>
<tr>
<th scope="row" colspan="6">{{( gettext "Total" )}}</th>
<td class="numeric">{{ .TotalAmount|formatPrice }}</td>
<td colspan="2"></td>
</tr>
</tfoot>
{{ end }}
</table>
{{- end }}

View File

@ -1,40 +0,0 @@
{{- range $invoice := .Invoices }}
<tr>
{{ $title := .Number | printf (pgettext "Select invoice %v" "action") }}
<td><input type="checkbox" form="batch-form"
name="invoice" value="{{ .Slug }}"
aria-label="{{ $title }}"
title="{{ $title }}"/></td>
<td>{{ .Date|formatDate }}</td>
<td><a href="/admin/invoices/{{ .Slug }}">{{ .Number }}</a></td>
<td>{{ .CustomerName }}</td>
<td class="invoice-status-{{ .Status }}">
<details class="invoice-status menu">
<summary>{{ .StatusLabel }}</summary>
<form data-hx-put="/admin/invoices/{{ .Slug }}">
{{ CSRFInput }}
<input type="hidden" name="quick" value="status">
<ul role="menu">
{{- range $status, $name := $.InvoiceStatuses }}
{{- if ne $status $invoice.Status }}
<li role="presentation">
<button role="menuitem" type="submit"
name="invoice_status" value="{{ $status }}"
class="invoice-status-{{ $status }}"
>{{ $name }}</button>
</li>
{{- end }}
{{- end }}
</ul>
</form>
</details>
</td>
{{- $title = .Number | printf (pgettext "Download invoice %s" "action") -}}
<td class="invoice-download"><a href="/admin/invoices/{{ .Slug }}.pdf"
download="{{ .Number}}-{{ .CustomerName | slugify }}.pdf"
title="{{( pgettext "Download invoice" "action" )}}"
aria-label="{{ $title }}">⤓</a></td>
<td class="numeric">{{ .Total|formatPrice }}</td>
</tr>
{{- end }}
{{ template "pagination" .Filters.Cursor | colspan 7 }}

View File

@ -79,7 +79,7 @@
<td>{{ .ZonePreferences }}</td>
</tr>
<tr>
<th scope="row">{{( pgettext "ACSI / ANWB card?" "input" )}}</th>
<th scope="row">{{( pgettext "ACSI card?" "input" )}}</th>
<td>{{if .ACSICard}}{{( gettext "Yes" )}}{{ else }}{{( gettext "No" )}}{{ end }}</td>
</tr>
<tr>

View File

@ -12,58 +12,6 @@
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/payment.paymentIndex*/ -}}
<a href="/admin/payments/settings">{{( pgettext "Payment Settings" "title" )}}</a>
{{ template "filters-toggle" }}
<form class="filters" method="GET" action="/admin/payments"
data-hx-target="main" data-hx-boost="true" data-hx-trigger="change,search,submit"
aria-labelledby="filters-toggle"
>
{{ with .Filters }}
<fieldset>
{{ with .PaymentStatus -}}
<label>
{{( pgettext "Payment status" "input" )}}<br>
<select name="{{ .Name }}"
{{ template "error-attrs" . }}
>
<option value="">{{( gettext "All statuses" )}}</option>
{{ template "list-options" . }}
</select><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .FromDate -}}
<label>
{{( pgettext "From date" "input" )}}<br>
<input type="date"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .ToDate -}}
<label>
{{( pgettext "To date" "input" )}}<br>
<input type="date"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .Reference -}}
<label>
{{( pgettext "Reference" "input" )}}<br>
<input type="search"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
</fieldset>
{{ end }}
{{ if .Filters.HasValue }}
<a href="/admin/payments" class="button">{{( pgettext "Reset" "action" )}}</a>
{{ end }}
</form>
<h2>{{ template "title" . }}</h2>
{{ if .Payments -}}
<table>
@ -77,7 +25,15 @@
</tr>
</thead>
<tbody>
{{ template "results.gohtml" . }}
{{ range .Payments -}}
<tr class="payment-{{ .Status }}">
<td>{{ .CreatedAt | formatDate }}</td>
<td><a href="{{ .URL }}">{{ .Reference }}</a></td>
<td class="payment-status">{{ .StatusLabel }}</td>
<td class="numeric">{{ .DownPayment | formatPrice }}</td>
<td class="numeric">{{ .Total | formatPrice }}</td>
</tr>
{{- end }}
</tbody>
</table>
{{ else -}}

View File

@ -1,10 +0,0 @@
{{ range .Payments -}}
<tr class="payment-{{ .Status }}">
<td>{{ .CreatedAt | formatDate }}</td>
<td><a href="{{ .URL }}">{{ .Reference }}</a></td>
<td class="payment-status">{{ .StatusLabel }}</td>
<td class="numeric">{{ .DownPayment | formatPrice }}</td>
<td class="numeric">{{ .Total | formatPrice }}</td>
</tr>
{{- end }}
{{ template "pagination" .Filters.Cursor | colspan 5 }}

View File

@ -12,39 +12,7 @@
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/user.loginAttemptIndex*/ -}}
{{ template "filters-toggle" }}
<form class="filters" method="GET" action="/admin/users/login-attempts"
data-hx-target="main" data-hx-boost="true" data-hx-trigger="change,search,submit"
aria-labelledby="filters-toggle"
>
{{ with .Filters }}
<fieldset>
{{ with .FromDate -}}
<label>
{{( pgettext "From date" "input" )}}<br>
<input type="date"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .ToDate -}}
<label>
{{( pgettext "To date" "input" )}}<br>
<input type="date"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
</fieldset>
{{ end }}
{{ if .Filters.HasValue }}
<a href="/admin/users/login-attempts" class="button">{{( pgettext "Reset" "action" )}}</a>
{{ end }}
</form>
<h2>{{( pgettext "Login Attempts" "title" )}}</h2>
{{ if .Attempts -}}
<table>
<thead>
<tr>
@ -55,10 +23,14 @@
</tr>
</thead>
<tbody>
{{ template "results.gohtml" . }}
{{ range . -}}
<tr>
<td>{{ .Date }}</td>
<td>{{ .UserName }}</td>
<td>{{ .IPAddress }}</td>
<td>{{ if .Success }}{{( gettext "Yes" )}}{{ else }}{{( gettext "No" )}}{{ end }}</td>
</tr>
{{- end }}
</tbody>
</table>
{{- else -}}
<p>{{( gettext "No logging attempts found." )}}</p>
{{- end }}
{{- end }}

View File

@ -1,9 +0,0 @@
{{ range .Attempts -}}
<tr>
<td>{{ .Date }}</td>
<td>{{ .UserName }}</td>
<td>{{ .IPAddress }}</td>
<td>{{ if .Success }}{{( gettext "Yes" )}}{{ else }}{{( gettext "No" )}}{{ end }}</td>
</tr>
{{- end }}
{{ template "pagination" .Filters.Cursor | colspan 4 }}

View File

@ -15,7 +15,7 @@
* {{( pgettext "Accommodation" "title" )}}: {{ .CampsiteType }}
* {{( pgettext "Area preferences" "header" )}}: {{ .ZonePreferences }}
* {{( pgettext "ACSI / ANWB card?" "input" )}}: {{if .ACSICard}}{{( gettext "Yes" )}}{{ else }}{{( gettext "No" )}}{{ end }}
* {{( pgettext "ACSI card?" "input" )}}: {{if .ACSICard}}{{( gettext "Yes" )}}{{ else }}{{( gettext "No" )}}{{ end }}
* {{( pgettext "Arrival date" "input" )}}: {{ .ArrivalDate.Format "02/01/2006" }}
* {{( pgettext "Departure date" "input" )}}: {{ .DepartureDate.Format "02/01/2006" }}
* {{( pgettext "Nights" "cart" )}}: {{ .NumNights }}

View File

@ -244,7 +244,7 @@
>
<input type="checkbox" name="{{ .Name }}" {{ if .Checked}}checked{{ end }}
{{ template "error-attrs" . }}
> {{( pgettext "ACSI / ANWB card? (optional)" "input" )}}<br>
> {{( pgettext "ACSI card? (optional)" "input" )}}<br>
{{ template "error-message" . }}
</label>
{{- end }}