guide changes

This commit is contained in:
2026-09-01 12:01:43 +05:30
parent ae6162a632
commit 485f31239d
11 changed files with 746 additions and 5 deletions

View File

@@ -444,7 +444,7 @@ func (ctl *TenantController) CreateTenantUser(c *fiber.Ctx) error {
func (ctl *TenantController) GetTenantInfo(c *fiber.Ctx) error {
log.Printf("[DEBUG] GetTenantInfo OriginalURL: %s, Headers: %v", c.OriginalURL(), c.GetReqHeaders())
// Parse tenant ID
tidStr := c.Query("tenantid")
if tidStr == "" {
@@ -580,3 +580,133 @@ func (ctl *TenantController) GetTenantByKeyword(c *fiber.Ctx) error {
"details": data,
})
}
// AssignStaff moves one of a merchant's people to a branch, or takes them off.
//
// `locationid` 0 unassigns, and is a real instruction rather than a missing
// value — somebody can leave a shop before the next one opens, and the console
// needs a way to say that which is not "delete the account".
//
// The tenant comes from the request and every check is scoped to it in the
// query, so a userid belonging to another business matches nothing and the call
// fails rather than moving a stranger's staff.
func (ctl *TenantController) AssignStaff(c *fiber.Ctx) error {
var input struct {
Tenantid int `json:"tenantid"`
Userid int `json:"userid"`
Locationid int `json:"locationid"`
Unassign bool `json:"unassign"`
}
if err := c.BodyParser(&input); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false, "message": "Invalid input",
})
}
if input.Tenantid <= 0 || input.Userid <= 0 {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false,
"message": "tenantid and userid are required",
})
}
// The rule lives in services.ResolveAssignment so it can be tested without
// a request: a zero locationid must never be read as "unassign", because a
// dropped field looks exactly like one.
location, err := services.ResolveAssignment(input.Locationid, input.Unassign)
if err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false, "message": err.Error(),
})
}
if err := ctl.tenantService.AssignStaffToBranch(input.Tenantid, input.Userid, location); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false, "message": err.Error(),
})
}
return c.JSON(fiber.Map{"code": 200, "status": true, "message": "Success"})
}
// UpdateTenantProfile lets a merchant change their own business record.
//
// The body is read as a free-form map rather than into `models.Tenants`,
// deliberately. Binding to the struct would make every column on the table a
// candidate for writing and leave "which of these may a merchant set?" answered
// by whichever fields a form happened to send. The allowlist in
// services.TenantProfileUpdate answers it in one place instead, and everything
// absent from a request is left alone rather than blanked.
func (ctl *TenantController) UpdateTenantProfile(c *fiber.Ctx) error {
fields := map[string]any{}
if err := c.BodyParser(&fields); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false, "message": "Invalid input",
})
}
// The row to write is named by `tenantid`, and it is the one value in the
// body that is never a value to write.
tenantID := 0
switch id := fields["tenantid"].(type) {
case float64:
tenantID = int(id)
case int:
tenantID = id
}
if tenantID <= 0 {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false,
"message": "tenantid is required",
})
}
if err := ctl.tenantService.UpdateTenantProfile(tenantID, fields); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false, "message": err.Error(),
})
}
return c.JSON(fiber.Map{"code": 200, "status": true, "message": "Success"})
}
// UpdateOwnProfile lets somebody change their own name, mobile or email.
//
// Read as a map rather than into `models.User` for the same reason as the shop
// profile: `app_users` keeps identity next to authorisation, so binding to the
// struct would make `roleid`, `locationid`, `status`, `password` and `pin`
// candidates for writing. The allowlist in services.OwnProfileUpdate answers
// "what may a person change about themselves?" in one place.
//
// Scoped by userid AND tenantid — the existing `users/update` checks only the
// userid, which is why the store user's account page has been read-only rather
// than editable.
func (ctl *TenantController) UpdateOwnProfile(c *fiber.Ctx) error {
fields := map[string]any{}
if err := c.BodyParser(&fields); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false, "message": "Invalid input",
})
}
readID := func(key string) int {
switch id := fields[key].(type) {
case float64:
return int(id)
case int:
return id
}
return 0
}
userID, tenantID := readID("userid"), readID("tenantid")
if userID <= 0 || tenantID <= 0 {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false,
"message": "userid and tenantid are both required",
})
}
if err := ctl.tenantService.UpdateOwnProfile(userID, tenantID, fields); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "status": false, "message": err.Error(),
})
}
return c.JSON(fiber.Map{"code": 200, "status": true, "message": "Success"})
}