delivery slot api creation

This commit is contained in:
2026-10-06 17:35:51 +05:30
parent f67cbad79a
commit 03ae7d310b
12 changed files with 1137 additions and 104 deletions

View File

@@ -0,0 +1,97 @@
package repositories
import (
"errors"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"nearle/models"
)
type DeliverySlotRepository interface {
ListForBranch(tenantID, locationID int) ([]models.DeliverySlots, error)
FindForBranch(tenantID, locationID, slotID int) (*models.DeliverySlots, error)
Save(slots []models.DeliverySlots) error
}
type deliverySlotRepository struct {
db *gorm.DB
}
func NewDeliverySlotRepository(db *gorm.DB) DeliverySlotRepository {
return &deliverySlotRepository{db: db}
}
// Ordered by start time so every caller — the console's editor and the app's
// list alike — sees morning before evening without sorting it again.
func (r *deliverySlotRepository) ListForBranch(tenantID, locationID int) ([]models.DeliverySlots, error) {
slots := make([]models.DeliverySlots, 0, len(models.SlotKeys))
err := r.db.Table("deliveryslots").
Where("tenantid = ? AND locationid = ?", tenantID, locationID).
Order("starttime ASC").
Find(&slots).Error
if err != nil {
return nil, err
}
return slots, nil
}
/*
One window, scoped to the branch that claims it.
The tenant and location are in the WHERE and not checked afterwards: a slot id
arrives from the app, which carries no session, so it is a claim about which
shop it belongs to. Looking it up by id alone and trusting the row would let one
shop's checkout name another shop's window.
*/
func (r *deliverySlotRepository) FindForBranch(tenantID, locationID, slotID int) (*models.DeliverySlots, error) {
var slot models.DeliverySlots
err := r.db.Table("deliveryslots").
Where("slotid = ? AND tenantid = ? AND locationid = ?", slotID, tenantID, locationID).
First(&slot).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
// Not an error: "this shop does not offer that" is an answer, and the
// service turns it into something a shopper can read.
return nil, nil
}
if err != nil {
return nil, err
}
return &slot, nil
}
/*
Write a branch's windows, all of them or none.
── Upsert, not delete-then-insert ──────────────────────────────────────────
Slot ids are referenced by `orders.deliveryslotid`. Replacing the rows would
renumber them, and every order already placed would point at a window that no
longer means what it did — or at nothing. The unique index on
(tenantid, locationid, slotkey) is what makes the conflict target work, so a
branch keeps one morning however many times it is edited.
── One transaction ─────────────────────────────────────────────────────────
A shop editing all three and getting two is worse than getting none: the two
that took are live and serving shoppers, and nothing on screen says which.
*/
func (r *deliverySlotRepository) Save(slots []models.DeliverySlots) error {
if len(slots) == 0 {
return nil
}
return r.db.Transaction(func(tx *gorm.DB) error {
return tx.Table("deliveryslots").
Clauses(clause.OnConflict{
Columns: []clause.Column{
{Name: "tenantid"}, {Name: "locationid"}, {Name: "slotkey"},
},
DoUpdates: clause.AssignmentColumns([]string{"name", "starttime", "endtime", "status", "updated"}),
}).
Create(&slots).Error
})
}