diff --git a/Dockerfile b/Dockerfile index 2a54e31..7801056 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ FROM golang:1.23-alpine AS builder RUN apk update && apk add --no-cache \ - build-base \ - ca-certificates \ - && update-ca-certificates + build-base \ + ca-certificates \ + && update-ca-certificates WORKDIR /app diff --git a/config/host.go b/config/host.go index b63a1cf..44f82c1 100644 --- a/config/host.go +++ b/config/host.go @@ -24,8 +24,8 @@ type HostItem struct { // If true, the item will be added to the calendar MustSyncCalendar bool HasEndDate bool - // If true, a tax item will be added to the invoice - Taxable bool // TODO: create taxes auto if taxable item + // Amount of taxes in EUR. If not zero, a tax item will be added to the invoice + Taxes float64 } func NewHost() *Host { @@ -47,7 +47,7 @@ func NewHost() *Host { CalendarId: os.Getenv("CALENDAR_ID_T2"), MustSyncCalendar: true, HasEndDate: true, - Taxable: true, + Taxes: 1.5, }, "T3": { @@ -56,7 +56,7 @@ func NewHost() *Host { CalendarId: os.Getenv("CALENDAR_ID_T3"), MustSyncCalendar: true, HasEndDate: true, - Taxable: true, + Taxes: 1.5, }, "Airport": { Name: "Airport", @@ -70,10 +70,6 @@ func NewHost() *Host { Name: "Transport", Price: 20.0, }, - "Taxes": { // TODO: remove after auto creation enabled - Name: "Taxes", - Price: 1.5, - }, }, } } diff --git a/internal/booking/service.go b/internal/booking/service.go index bc9d6ec..374d4b8 100644 --- a/internal/booking/service.go +++ b/internal/booking/service.go @@ -20,8 +20,8 @@ type Service struct { db *gorm.DB } -func NewService(db *gorm.DB) *Service { - return &Service{db: db} +func NewService(db *gorm.DB) (*Service, error) { + return &Service{db: db}, nil } func (bs Service) All() []*Line { @@ -97,16 +97,30 @@ func (bs Service) Update(id int, From time.Time, To time.Time, Name string, Phon return b } -func (bs Service) CreateItem(bid int, item string, qty int, price float64, method string) *Item { +func (bs Service) CreateItem(bookingId int, item config.HostItem, quantity int, price float64, paymentMethod string, customerNumber int) (items []*Item) { i := &Item{ - BookingId: bid, - Item: item, - Quantity: qty, + BookingId: bookingId, + Item: item.Name, + Quantity: quantity, Price: price, - PaymentMethod: method, + PaymentMethod: paymentMethod, } _ = bs.db.Create(i) - return i + items = append(items, i) + + if item.Taxes != 0.0 { + ti := &Item{ + BookingId: bookingId, + Item: "Taxes", + Quantity: quantity * customerNumber, + Price: item.Taxes, + PaymentMethod: "Cash", + } + _ = bs.db.Create(ti) + items = append(items, ti) + } + + return items } func (bs Service) PayItem(id int) *Item { @@ -257,20 +271,19 @@ func (bs Service) ParseFromApi(rawContent string) (*Booking, error) { arrivalDate := extractDate(`Date d'arrivée `, content) departureDate := extractDate(`Date de départ `, content) stayLength := extractInt(`Durée de séjour : (\d+) nuits`, content) - totalAmount := extractFloat(`Montant total € (\d+)`, content) customerName := extractString(`Nom du client : \n\s+([\w\s]+)`, content) customerName = strings.SplitN(customerName, "\n", 2)[0] customerEmail := extractString(`[\w\.\-]+@[\w\.\-]+\.\w+`, content) customerNumber := extractInt(`Nombre de personnes : \s*\n\s*(\d+)`, content) commissionAmount := extractFloat(`Commission : € (\d+,\d+)`, content) - item := extractString(`Maison 1 Chambre \((T2|T3) -`, content) + itemName := extractString(`Maison 1 Chambre \((T2|T3) -`, content) externalId := extractString(`Numéro de réservation : \n\s+(\d+)`, content) standardRate := extractFloat(`Standard Rate\n\s+€ (\d+)`, content) - taxQty := (totalAmount - standardRate*float64(stayLength)) / 1.5 b := bs.Create(*formatDate(arrivalDate), *formatDate(departureDate), customerName, "", customerEmail, "Booking", customerNumber, commissionAmount, &externalId) - bs.CreateItem(b.Id, item, stayLength, standardRate, "Card") - bs.CreateItem(b.Id, "Taxes", int(taxQty), 1.5, "Cash") + if item, ok := config.NewHost().Items[itemName]; ok { + bs.CreateItem(b.Id, item, stayLength, standardRate, "Card", customerNumber) + } return b, nil } diff --git a/internal/cron/job_report.go b/internal/cron/job_report.go index 873680f..1e0df05 100644 --- a/internal/cron/job_report.go +++ b/internal/cron/job_report.go @@ -23,7 +23,8 @@ func JobMonthlyBookingReport() error { now := time.Now() log.Println("Start Monthly Booking Report job at:", now) - report := booking.NewService(db).BuildReport("monthly", int(now.Month()), now.Year()) + service, _ := booking.NewService(db) + report := service.BuildReport("monthly", int(now.Month()), now.Year()) ps, err := pdf.NewPdfService( os.Getenv("HTMLDOCS_PROJECT_ID"), diff --git a/internal/server/handle_bookings.go b/internal/server/handle_bookings.go index 65dd907..15fc027 100644 --- a/internal/server/handle_bookings.go +++ b/internal/server/handle_bookings.go @@ -139,13 +139,13 @@ func handleBookingPage(bs *booking.Service, hc *config.Host) echo.HandlerFunc { return sum + i.Price*float64(i.Quantity) }, 0.0), 'f', 2, 64), Platforms: hc.Platforms, - ItemList: u.OrderBy(func(items map[string]config.HostItem) (out []string) { + ItemList: u.OrderBy(func(items map[string]config.HostItem) (out []string) { // TODO: return the full item to prefill the form for _, item := range items { out = append(out, item.Name) } return out }(hc.Items), - func(l, r string) bool { return l < r }, + func(l, r string) bool { return l > r }, ), PaymentMethods: hc.PaymentMethods, } @@ -257,7 +257,7 @@ func handleCreateItem(bs *booking.Service, cs *calendar.Service, hc *config.Host return fmt.Errorf("invalid item name %q", ni.Item) } - i := bs.CreateItem(b.Id, ni.Item, ni.Quantity, ni.Price, ni.PaymentMethod) + newItems := bs.CreateItem(b.Id, itm, ni.Quantity, ni.Price, ni.PaymentMethod, b.CustomerNumber) if err = cs.Create( itm.CalendarId, @@ -269,16 +269,20 @@ func handleCreateItem(bs *booking.Service, cs *calendar.Service, hc *config.Host captureError(c, err) } - return renderTempl(c, http.StatusCreated, view.LineItem(&view.ItemViewModel{ - Id: strconv.Itoa(i.Id), - Item: i.Item, - Quantity: strconv.Itoa(i.Quantity), - Price: strconv.FormatFloat(i.Price, 'f', 2, 64), - PaymentMethod: i.PaymentMethod, - PaymentStatus: i.PaymentStatus, - SubTotal: strconv.FormatFloat(i.Price*float64(i.Quantity), 'f', 2, 64), - ItemUrl: fmt.Sprintf("%s/%d", constant.RouteItem, i.Id), - })) + for _, i := range newItems { + _ = renderTempl(c, http.StatusCreated, view.LineItem(&view.ItemViewModel{ + Id: strconv.Itoa(i.Id), + Item: i.Item, + Quantity: strconv.Itoa(i.Quantity), + Price: strconv.FormatFloat(i.Price, 'f', 2, 64), + PaymentMethod: i.PaymentMethod, + PaymentStatus: i.PaymentStatus, + SubTotal: strconv.FormatFloat(i.Price*float64(i.Quantity), 'f', 2, 64), + ItemUrl: fmt.Sprintf("%s/%d", constant.RouteItem, i.Id), + })) + } + + return nil } } diff --git a/internal/view/layout/public_templ.go b/internal/view/layout/public_templ.go index 8c37ccd..7339778 100644 --- a/internal/view/layout/public_templ.go +++ b/internal/view/layout/public_templ.go @@ -74,7 +74,7 @@ func PublicLayout() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" | Locations de vacances au Gosier en Guadeloupe