From 542dbf0f9757ead6083660c664ce64e0845972bf Mon Sep 17 00:00:00 2001 From: Ruidy Date: Sun, 8 Sep 2024 23:12:13 +0200 Subject: [PATCH] split the service layer --- internal/booking/item.go | 61 ++++++++ internal/booking/line.go | 35 +++++ internal/booking/report.go | 56 +++++++ internal/booking/service.go | 234 ----------------------------- internal/booking/sync.go | 100 ++++++++++++ internal/server/handle_api_sync.go | 15 +- 6 files changed, 266 insertions(+), 235 deletions(-) create mode 100644 internal/booking/item.go create mode 100644 internal/booking/line.go create mode 100644 internal/booking/report.go create mode 100644 internal/booking/sync.go diff --git a/internal/booking/item.go b/internal/booking/item.go new file mode 100644 index 0000000..0c301e0 --- /dev/null +++ b/internal/booking/item.go @@ -0,0 +1,61 @@ +package booking + +import ( + "log" + + "github.com/rjNemo/rentease/internal/config" +) + +func (bs Service) CreateItem(bookingId int, item config.HostItem, quantity int, price float64, paymentMethod string, customerNumber int) (items []*Item) { + i := &Item{ + BookingId: bookingId, + Item: item.Name, + Quantity: quantity, + Price: price, + PaymentMethod: paymentMethod, + } + if err := bs.store.CreateItem(i); err != nil { + log.Println(err) + } + items = append(items, i) + + if item.Taxes != 0.0 { + ti := &Item{ + BookingId: bookingId, + Item: "Taxes", + Quantity: quantity * customerNumber, + Price: item.Taxes, + PaymentMethod: "Cash", + } + if err := bs.store.CreateItem(ti); err != nil { + log.Println(err) + } + items = append(items, ti) + } + + return items +} + +func (bs Service) PayItem(id int) *Item { + i, err := bs.store.PayItem(id) + if err != nil { + log.Println(err) + } + return i +} + +func (bs Service) OneItem(id int) *Item { + i, err := bs.store.GetItem(id) + if err != nil { + log.Println(err) + } + return i +} + +func (bs Service) UpdateItem(id int, item string, qty int, price float64, paymentMethod, paymentStatus string) *Item { + i, err := bs.store.UpdateItem(id, item, paymentMethod, paymentStatus, qty, price) + if err != nil { + log.Println(err) + } + return i +} diff --git a/internal/booking/line.go b/internal/booking/line.go new file mode 100644 index 0000000..f686c43 --- /dev/null +++ b/internal/booking/line.go @@ -0,0 +1,35 @@ +package booking + +import ( + "fmt" + "strconv" + "time" + + "github.com/rjNemo/rentease/internal/config" +) + +type Line struct { + From time.Time + To time.Time + CustomerName string + Platform string + Id int + Total float64 + PlatformFees float64 + Canceled bool +} + +func (l Line) InvoiceNumber(hc *config.Host) string { + return fmt.Sprintf("%s%04s", hc.InvoicePrefix, strconv.Itoa(l.Id+hc.CustomerSeed)) +} + +func (l Line) Fee() float64 { + if l.Platform == "Other" { + return l.Total * 5 / 100 + } + return l.Total * 10 / 100 +} + +func (l Line) Profit() float64 { + return l.Total - l.PlatformFees - l.Fee() +} diff --git a/internal/booking/report.go b/internal/booking/report.go new file mode 100644 index 0000000..34df037 --- /dev/null +++ b/internal/booking/report.go @@ -0,0 +1,56 @@ +package booking + +import ( + "log" + "time" + + u "github.com/rjNemo/underscore" +) + +type Report struct { + Lines []*Line + Total float64 + PlatformFees float64 + Fee float64 + Profit float64 + CardTotal float64 + BookingFees float64 +} + +func (bs Service) BuildReport(period string, month, year int) *Report { + var startDate time.Time + var endDate time.Time + + if period == "month" { + startDate = time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) + endDate = time.Date(year, time.Month(month)+1, 1, 0, 0, 0, 0, time.UTC).Add(-24 * time.Hour) + } else { + startDate = time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC) + endDate = time.Date(year, time.December, 31, 0, 0, 0, 0, time.UTC) + } + + lines, err := bs.store.List(startDate, endDate) + if err != nil { + log.Println(err) + } + + cardTotal, err := bs.store.CardTotal(startDate, endDate) + if err != nil { + log.Println(err) + } + + return &Report{ + Total: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.Total }, 0.0), + PlatformFees: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.PlatformFees }, 0.0), + Fee: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.Fee() }, 0.0), + Profit: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.Profit() }, 0.0), + Lines: lines, + CardTotal: cardTotal, + BookingFees: u.Reduce(lines, func(l *Line, sum float64) float64 { + if l.Platform == "Booking" { + return sum + l.PlatformFees + } + return sum + }, 0.0), + } +} diff --git a/internal/booking/service.go b/internal/booking/service.go index 174ef29..dc279a6 100644 --- a/internal/booking/service.go +++ b/internal/booking/service.go @@ -1,18 +1,10 @@ package booking import ( - "encoding/json" - "fmt" "log" - "regexp" - "strconv" - "strings" "time" - u "github.com/rjNemo/underscore" "gorm.io/gorm" - - "github.com/rjNemo/rentease/internal/config" ) type Service struct { @@ -79,134 +71,6 @@ func (bs Service) Update(id int, From time.Time, To time.Time, Name string, Phon return b } -func (bs Service) CreateItem(bookingId int, item config.HostItem, quantity int, price float64, paymentMethod string, customerNumber int) (items []*Item) { - i := &Item{ - BookingId: bookingId, - Item: item.Name, - Quantity: quantity, - Price: price, - PaymentMethod: paymentMethod, - } - if err := bs.store.CreateItem(i); err != nil { - log.Println(err) - } - items = append(items, i) - - if item.Taxes != 0.0 { - ti := &Item{ - BookingId: bookingId, - Item: "Taxes", - Quantity: quantity * customerNumber, - Price: item.Taxes, - PaymentMethod: "Cash", - } - if err := bs.store.CreateItem(ti); err != nil { - log.Println(err) - } - items = append(items, ti) - } - - return items -} - -func (bs Service) PayItem(id int) *Item { - i, err := bs.store.PayItem(id) - if err != nil { - log.Println(err) - } - return i -} - -func (bs Service) OneItem(id int) *Item { - i, err := bs.store.GetItem(id) - if err != nil { - log.Println(err) - } - return i -} - -func (bs Service) UpdateItem(id int, item string, qty int, price float64, paymentMethod, paymentStatus string) *Item { - i, err := bs.store.UpdateItem(id, item, paymentMethod, paymentStatus, qty, price) - if err != nil { - log.Println(err) - } - return i -} - -type Report struct { - Lines []*Line - Total float64 - PlatformFees float64 - Fee float64 - Profit float64 - CardTotal float64 - BookingFees float64 -} - -type Line struct { - From time.Time - To time.Time - CustomerName string - Platform string - Id int - Total float64 - PlatformFees float64 - Canceled bool -} - -func (l Line) InvoiceNumber(hc *config.Host) string { - return fmt.Sprintf("%s%04s", hc.InvoicePrefix, strconv.Itoa(l.Id+hc.CustomerSeed)) -} - -func (l Line) Fee() float64 { - if l.Platform == "Other" { - return l.Total * 5 / 100 - } - return l.Total * 10 / 100 -} - -func (l Line) Profit() float64 { - return l.Total - l.PlatformFees - l.Fee() -} - -func (bs Service) BuildReport(period string, month, year int) *Report { - var startDate time.Time - var endDate time.Time - - if period == "month" { - startDate = time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) - endDate = time.Date(year, time.Month(month)+1, 1, 0, 0, 0, 0, time.UTC).Add(-24 * time.Hour) - } else { - startDate = time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC) - endDate = time.Date(year, time.December, 31, 0, 0, 0, 0, time.UTC) - } - - lines, err := bs.store.List(startDate, endDate) - if err != nil { - log.Println(err) - } - - cardTotal, err := bs.store.CardTotal(startDate, endDate) - if err != nil { - log.Println(err) - } - - return &Report{ - Total: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.Total }, 0.0), - PlatformFees: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.PlatformFees }, 0.0), - Fee: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.Fee() }, 0.0), - Profit: u.Reduce(lines, func(l *Line, sum float64) float64 { return sum + l.Profit() }, 0.0), - Lines: lines, - CardTotal: cardTotal, - BookingFees: u.Reduce(lines, func(l *Line, sum float64) float64 { - if l.Platform == "Booking" { - return sum + l.PlatformFees - } - return sum - }, 0.0), - } -} - // func (bs Service) CreateRequest(From time.Time, To time.Time, Name string, PhoneNumber string, Email string, Item string, CustomerNumber int) *BookingRequest { // b := &BookingRequest{ // CustomerName: Name, @@ -227,101 +91,3 @@ func (bs Service) Cancel(id int) { log.Println(err) } } - -func (bs Service) ParseFromApi(rawContent string) (*Booking, error) { - type BookingInfo struct { - Content string `json:"content"` - } - - var bookingInfo BookingInfo - err := json.Unmarshal([]byte(rawContent), &bookingInfo) - if err != nil { - return nil, fmt.Errorf("error unmarshalling JSON: %w", err) - } - - content := strings.ReplaceAll(strings.TrimSpace(bookingInfo.Content), "\u00a0", " ") - - arrivalDate := extractDate(`Date d'arrivée `, content) - departureDate := extractDate(`Date de départ `, content) - stayLength := extractInt(`Durée de séjour : (\d+) nuits`, 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) - itemName := extractString(`Maison . Chambre. \((T2|T3) -`, content) - externalId := extractString(`Numéro de réservation : \n\s+(\d+)`, content) - standardRate := extractFloat(`Standard Rate\n\s+€ (\d+)`, content) - - b := bs.Create(*formatDate(arrivalDate), *formatDate(departureDate), customerName, "", customerEmail, "Booking", customerNumber, commissionAmount, &externalId) - if item, ok := config.NewHost().Items[itemName]; ok { - bs.CreateItem(b.Id, item, stayLength, standardRate, "Card", customerNumber) - } - - return b, nil -} - -func extractDate(pattern, content string) string { - re := regexp.MustCompile(pattern + `(lun|mar|mer|jeu|ven|sam|dim)\. \d{1,2} (janv|févr|mars|avr|mai|juin|juil|août|sept|oct|nov|déc)\.? \d{4}`) - dateMatch := re.FindString(content) - - if dateMatch == "" { - log.Println("date not found") - return "" - } - - // Regular expression to remove the prefix - rePrefix := regexp.MustCompile(pattern + `\w+\.\s*`) - dateString := rePrefix.ReplaceAllString(dateMatch, "") - return dateString -} - -func extractInt(pattern, content string) int { - re := regexp.MustCompile(pattern) - match := re.FindStringSubmatch(content) - if len(match) > 1 { - val, err := strconv.Atoi(match[1]) - if err == nil { - return val - } - } - return 0 -} - -func extractFloat(pattern, content string) float64 { - re := regexp.MustCompile(pattern) - match := re.FindStringSubmatch(content) - if len(match) > 1 { - val, err := strconv.ParseFloat(strings.ReplaceAll(match[1], ",", "."), 64) - if err == nil { - return val - } - } - return 0.0 -} - -func extractString(pattern, content string) string { - re := regexp.MustCompile(pattern) - match := re.FindStringSubmatch(content) - if len(match) > 1 { - return strings.TrimSpace(match[1]) - } - return strings.TrimSpace(match[0]) -} - -func formatDate(date string) *time.Time { - months := map[string]string{ - "janv.": "01", "févr.": "02", "mar": "03", "avr": "04", - "mai": "05", "juin": "06", "juil.": "07", "août": "08", - "sep": "09", "oct": "10", "nov": "11", "déc": "12", - } - parts := strings.Split(date, " ") - dateString := fmt.Sprintf("%s-%02s-%02s", parts[2], months[parts[1]], parts[0]) - - t, err := time.Parse(time.DateOnly, dateString) - if err != nil { - log.Println(err) - return nil - } - return &t -} diff --git a/internal/booking/sync.go b/internal/booking/sync.go new file mode 100644 index 0000000..f86a056 --- /dev/null +++ b/internal/booking/sync.go @@ -0,0 +1,100 @@ +package booking + +import ( + "fmt" + "log" + "regexp" + "strconv" + "strings" + "time" + + "github.com/rjNemo/rentease/internal/config" +) + +func (bs Service) ParseFromApi(rawContent string) (*Booking, error) { + content := strings.ReplaceAll(strings.TrimSpace(rawContent), "\u00a0", " ") + + arrivalDate := extractDate(`Date d'arrivée `, content) + departureDate := extractDate(`Date de départ `, content) + stayLength := extractInt(`Durée de séjour : (\d+) nuits`, 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) + itemName := extractString(`Maison . Chambre. \((T2|T3) -`, content) + externalId := extractString(`Numéro de réservation : \n\s+(\d+)`, content) + standardRate := extractFloat(`Standard Rate\n\s+€ (\d+)`, content) + + b := bs.Create(*formatDate(arrivalDate), *formatDate(departureDate), customerName, "", customerEmail, "Booking", customerNumber, commissionAmount, &externalId) + if item, ok := config.NewHost().Items[itemName]; ok { + bs.CreateItem(b.Id, item, stayLength, standardRate, "Card", customerNumber) + } + + return b, nil +} + +func extractDate(pattern, content string) string { + re := regexp.MustCompile(pattern + `(lun|mar|mer|jeu|ven|sam|dim)\. \d{1,2} (janv|févr|mars|avr|mai|juin|juil|août|sept|oct|nov|déc)\.? \d{4}`) + dateMatch := re.FindString(content) + + if dateMatch == "" { + log.Println("date not found") + return "" + } + + // Regular expression to remove the prefix + rePrefix := regexp.MustCompile(pattern + `\w+\.\s*`) + dateString := rePrefix.ReplaceAllString(dateMatch, "") + return dateString +} + +func extractInt(pattern, content string) int { + re := regexp.MustCompile(pattern) + match := re.FindStringSubmatch(content) + if len(match) > 1 { + val, err := strconv.Atoi(match[1]) + if err == nil { + return val + } + } + return 0 +} + +func extractFloat(pattern, content string) float64 { + re := regexp.MustCompile(pattern) + match := re.FindStringSubmatch(content) + if len(match) > 1 { + val, err := strconv.ParseFloat(strings.ReplaceAll(match[1], ",", "."), 64) + if err == nil { + return val + } + } + return 0.0 +} + +func extractString(pattern, content string) string { + re := regexp.MustCompile(pattern) + match := re.FindStringSubmatch(content) + if len(match) > 1 { + return strings.TrimSpace(match[1]) + } + return strings.TrimSpace(match[0]) +} + +func formatDate(date string) *time.Time { + months := map[string]string{ + "janv.": "01", "févr.": "02", "mar": "03", "avr": "04", + "mai": "05", "juin": "06", "juil.": "07", "août": "08", + "sep": "09", "oct": "10", "nov": "11", "déc": "12", + } + parts := strings.Split(date, " ") + dateString := fmt.Sprintf("%s-%02s-%02s", parts[2], months[parts[1]], parts[0]) + + t, err := time.Parse(time.DateOnly, dateString) + if err != nil { + log.Println(err) + return nil + } + return &t +} diff --git a/internal/server/handle_api_sync.go b/internal/server/handle_api_sync.go index a190e30..45997d4 100644 --- a/internal/server/handle_api_sync.go +++ b/internal/server/handle_api_sync.go @@ -1,6 +1,8 @@ package server import ( + "encoding/json" + "fmt" "io" "net/http" @@ -11,6 +13,10 @@ import ( ) func handleSync(bs *booking.Service) echo.HandlerFunc { + type BookingInfo struct { + Content string `json:"content"` + } + return func(c echo.Context) error { log.Info("received booking sync request from booking") x := c.Request().Body @@ -18,7 +24,14 @@ func handleSync(bs *booking.Service) echo.HandlerFunc { if err != nil { return c.String(http.StatusInternalServerError, err.Error()) } - b, err := bs.ParseFromApi(string(body)) + + bookingInfo := new(BookingInfo) + err = json.Unmarshal(body, bookingInfo) + if err != nil { + return c.String(http.StatusInternalServerError, fmt.Sprintf("error unmarshalling JSON: %w", err)) + } + + b, err := bs.ParseFromApi(bookingInfo.Content) if err != nil { return c.String(http.StatusInternalServerError, err.Error()) }