feat: add invoice-configs management functionality

This commit is contained in:
Tran Anh Tuan
2026-05-12 09:25:03 +07:00
parent 0ff65a18c0
commit eac8a686d1
14 changed files with 1534 additions and 1 deletions

View File

@@ -20,6 +20,7 @@ const (
API_GROUP_COMPONENT = "/components" API_GROUP_COMPONENT = "/components"
API_GROUP_COMPONENT_CODE = "/component-codes" API_GROUP_COMPONENT_CODE = "/component-codes"
API_GROUP_COMPONENT_ITEM = "/component-items" API_GROUP_COMPONENT_ITEM = "/component-items"
API_GROUP_INVOICE_CONFIG = "/invoice-configs"
) )
const ( const (

View File

@@ -0,0 +1,34 @@
-- name: GetInvoiceConfigByID :one
SELECT * FROM invoice_configs
WHERE id = sqlc.arg(id);
-- name: ListInvoiceConfigs :many
SELECT * FROM invoice_configs
ORDER BY created_at DESC;
-- name: CreateInvoiceConfig :one
INSERT INTO invoice_configs (name, type, description, is_active, metadata, created_at)
VALUES (
sqlc.arg(name),
sqlc.arg(type),
sqlc.arg(description),
sqlc.arg(is_active),
sqlc.arg(metadata),
sqlc.arg(created_at)
)
RETURNING *;
-- name: UpdateInvoiceConfig :one
UPDATE invoice_configs
SET name = CASE WHEN sqlc.arg(name) = '' THEN name ELSE sqlc.arg(name) END,
type = coalesce(sqlc.arg(type), type),
description = coalesce(sqlc.arg(description), description),
is_active = coalesce(sqlc.arg(is_active), is_active),
metadata = coalesce(sqlc.arg(metadata), metadata),
updated_at = sqlc.arg(updated_at)
WHERE id = sqlc.arg(id)
RETURNING *;
-- name: DeleteInvoiceConfig :execrows
DELETE FROM invoice_configs
WHERE id = sqlc.arg(id);

View File

@@ -1873,6 +1873,279 @@ const docTemplate = `{
} }
} }
}, },
"/v1/invoice-configs": {
"get": {
"description": "Retrieve a list of all invoice configs ordered by creation date",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "List all invoice configs",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/models.InvoiceConfig"
}
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"post": {
"description": "Create a new invoice config with the provided details",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Create a new invoice config",
"parameters": [
{
"description": "Invoice config request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.CreateInvoiceConfigRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.CreateInvoiceConfigResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/v1/invoice-configs/{id}": {
"get": {
"description": "Retrieve a single invoice config using its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Get invoice config by ID",
"parameters": [
{
"type": "integer",
"description": "Invoice config ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/models.InvoiceConfig"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"put": {
"description": "Update an existing invoice config by its ID. Only non-empty fields will be updated.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Update invoice config",
"parameters": [
{
"type": "integer",
"description": "Invoice config ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Invoice config request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.UpdateInvoiceConfigRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.UpdateInvoiceConfigResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"delete": {
"description": "Delete an invoice config by its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Delete invoice config",
"parameters": [
{
"type": "integer",
"description": "Invoice config ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.SuccessResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/v1/rooms": { "/v1/rooms": {
"get": { "get": {
"description": "Retrieve a list of all rooms ordered by creation date", "description": "Retrieve a list of all rooms ordered by creation date",
@@ -2906,6 +3179,38 @@ const docTemplate = `{
} }
} }
}, },
"models.InvoiceConfig": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"isActive": {
"type": "boolean"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"models.Room": { "models.Room": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3158,6 +3463,27 @@ const docTemplate = `{
} }
} }
}, },
"requests.CreateInvoiceConfigRequest": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"description": {
"type": "string"
},
"isActive": {
"type": "boolean"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"requests.CreateRoomRequest": { "requests.CreateRoomRequest": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -3357,6 +3683,23 @@ const docTemplate = `{
} }
} }
}, },
"requests.UpdateInvoiceConfigRequest": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"isActive": {
"type": "boolean"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"requests.UpdateRoomRequest": { "requests.UpdateRoomRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3485,6 +3828,14 @@ const docTemplate = `{
} }
} }
}, },
"responses.CreateInvoiceConfigResponse": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"responses.CreateRoomResponse": { "responses.CreateRoomResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3664,6 +4015,26 @@ const docTemplate = `{
} }
} }
}, },
"responses.UpdateInvoiceConfigResponse": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"isActive": {
"type": "boolean"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"responses.UpdateRoomResponse": { "responses.UpdateRoomResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -1867,6 +1867,279 @@
} }
} }
}, },
"/v1/invoice-configs": {
"get": {
"description": "Retrieve a list of all invoice configs ordered by creation date",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "List all invoice configs",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/models.InvoiceConfig"
}
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"post": {
"description": "Create a new invoice config with the provided details",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Create a new invoice config",
"parameters": [
{
"description": "Invoice config request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.CreateInvoiceConfigRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.CreateInvoiceConfigResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/v1/invoice-configs/{id}": {
"get": {
"description": "Retrieve a single invoice config using its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Get invoice config by ID",
"parameters": [
{
"type": "integer",
"description": "Invoice config ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/models.InvoiceConfig"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"put": {
"description": "Update an existing invoice config by its ID. Only non-empty fields will be updated.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Update invoice config",
"parameters": [
{
"type": "integer",
"description": "Invoice config ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Invoice config request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.UpdateInvoiceConfigRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.UpdateInvoiceConfigResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"delete": {
"description": "Delete an invoice config by its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"invoice-config"
],
"summary": "Delete invoice config",
"parameters": [
{
"type": "integer",
"description": "Invoice config ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.SuccessResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/v1/rooms": { "/v1/rooms": {
"get": { "get": {
"description": "Retrieve a list of all rooms ordered by creation date", "description": "Retrieve a list of all rooms ordered by creation date",
@@ -2900,6 +3173,38 @@
} }
} }
}, },
"models.InvoiceConfig": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"isActive": {
"type": "boolean"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"models.Room": { "models.Room": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3152,6 +3457,27 @@
} }
} }
}, },
"requests.CreateInvoiceConfigRequest": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"description": {
"type": "string"
},
"isActive": {
"type": "boolean"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"requests.CreateRoomRequest": { "requests.CreateRoomRequest": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -3351,6 +3677,23 @@
} }
} }
}, },
"requests.UpdateInvoiceConfigRequest": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"isActive": {
"type": "boolean"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"requests.UpdateRoomRequest": { "requests.UpdateRoomRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3479,6 +3822,14 @@
} }
} }
}, },
"responses.CreateInvoiceConfigResponse": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"responses.CreateRoomResponse": { "responses.CreateRoomResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3658,6 +4009,26 @@
} }
} }
}, },
"responses.UpdateInvoiceConfigResponse": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"isActive": {
"type": "boolean"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"responses.UpdateRoomResponse": { "responses.UpdateRoomResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -143,6 +143,27 @@ definitions:
warehouseName: warehouseName:
type: string type: string
type: object type: object
models.InvoiceConfig:
properties:
createdAt:
type: string
description:
type: string
id:
type: integer
isActive:
type: boolean
metadata:
items:
type: integer
type: array
name:
type: string
type:
type: string
updatedAt:
type: string
type: object
models.Room: models.Room:
properties: properties:
createdAt: createdAt:
@@ -312,6 +333,20 @@ definitions:
- name - name
- shelfId - shelfId
type: object type: object
requests.CreateInvoiceConfigRequest:
properties:
description:
type: string
isActive:
type: boolean
name:
type: string
type:
type: string
required:
- name
- type
type: object
requests.CreateRoomRequest: requests.CreateRoomRequest:
properties: properties:
description: description:
@@ -444,6 +479,17 @@ definitions:
name: name:
type: string type: string
type: object type: object
requests.UpdateInvoiceConfigRequest:
properties:
description:
type: string
isActive:
type: boolean
name:
type: string
type:
type: string
type: object
requests.UpdateRoomRequest: requests.UpdateRoomRequest:
properties: properties:
description: description:
@@ -526,6 +572,11 @@ definitions:
id: id:
type: integer type: integer
type: object type: object
responses.CreateInvoiceConfigResponse:
properties:
id:
type: integer
type: object
responses.CreateRoomResponse: responses.CreateRoomResponse:
properties: properties:
id: id:
@@ -642,6 +693,19 @@ definitions:
shelfId: shelfId:
type: integer type: integer
type: object type: object
responses.UpdateInvoiceConfigResponse:
properties:
description:
type: string
id:
type: integer
isActive:
type: boolean
name:
type: string
type:
type: string
type: object
responses.UpdateRoomResponse: responses.UpdateRoomResponse:
properties: properties:
description: description:
@@ -1844,6 +1908,176 @@ paths:
summary: Update container summary: Update container
tags: tags:
- container - container
/v1/invoice-configs:
get:
consumes:
- application/json
description: Retrieve a list of all invoice configs ordered by creation date
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.SuccessResponse'
- properties:
data:
items:
$ref: '#/definitions/models.InvoiceConfig'
type: array
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: List all invoice configs
tags:
- invoice-config
post:
consumes:
- application/json
description: Create a new invoice config with the provided details
parameters:
- description: Invoice config request body
in: body
name: body
required: true
schema:
$ref: '#/definitions/requests.CreateInvoiceConfigRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
allOf:
- $ref: '#/definitions/response.SuccessResponse'
- properties:
data:
$ref: '#/definitions/responses.CreateInvoiceConfigResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: Create a new invoice config
tags:
- invoice-config
/v1/invoice-configs/{id}:
delete:
consumes:
- application/json
description: Delete an invoice config by its unique identifier
parameters:
- description: Invoice config ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.SuccessResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: Delete invoice config
tags:
- invoice-config
get:
consumes:
- application/json
description: Retrieve a single invoice config using its unique identifier
parameters:
- description: Invoice config ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.SuccessResponse'
- properties:
data:
$ref: '#/definitions/models.InvoiceConfig'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: Get invoice config by ID
tags:
- invoice-config
put:
consumes:
- application/json
description: Update an existing invoice config by its ID. Only non-empty fields
will be updated.
parameters:
- description: Invoice config ID
in: path
name: id
required: true
type: integer
- description: Invoice config request body
in: body
name: body
required: true
schema:
$ref: '#/definitions/requests.UpdateInvoiceConfigRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.SuccessResponse'
- properties:
data:
$ref: '#/definitions/responses.UpdateInvoiceConfigResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: Update invoice config
tags:
- invoice-config
/v1/rooms: /v1/rooms:
get: get:
consumes: consumes:

View File

@@ -0,0 +1,50 @@
package mapper
import (
"wm-backend/internal/models"
db "wm-backend/sqlc_gen"
"github.com/jackc/pgx/v5/pgtype"
)
func ToDomainInvoiceConfig(r db.InvoiceConfig) *models.InvoiceConfig {
return &models.InvoiceConfig{
ID: r.ID,
Name: r.Name,
Type: string(r.Type),
Description: r.Description.String,
IsActive: r.IsActive,
Metadata: r.Metadata,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
}
}
func ToModelInvoiceConfig(r *models.InvoiceConfig) *db.CreateInvoiceConfigParams {
return &db.CreateInvoiceConfigParams{
Name: r.Name,
Type: db.InvoiceTypeEnum(r.Type),
Description: pgtype.Text{
String: r.Description,
Valid: r.Description != "",
},
IsActive: r.IsActive,
Metadata: r.Metadata,
CreatedAt: r.CreatedAt,
}
}
func ToUpdateModelInvoiceConfig(r *models.InvoiceConfig) *db.UpdateInvoiceConfigParams {
return &db.UpdateInvoiceConfigParams{
Name: r.Name,
Type: db.InvoiceTypeEnum(r.Type),
Description: pgtype.Text{
String: r.Description,
Valid: r.Description != "",
},
IsActive: r.IsActive,
Metadata: r.Metadata,
UpdatedAt: r.UpdatedAt,
ID: r.ID,
}
}

View File

@@ -0,0 +1,14 @@
package models
import "time"
type InvoiceConfig struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
IsActive bool `json:"isActive"`
Metadata []byte `json:"metadata"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}

View File

@@ -0,0 +1,15 @@
package requests
type CreateInvoiceConfigRequest struct {
Name string `json:"name" binding:"required"`
Type string `json:"type" binding:"required"`
Description string `json:"description"`
IsActive bool `json:"isActive"`
}
type UpdateInvoiceConfigRequest struct {
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
IsActive *bool `json:"isActive"`
}

View File

@@ -0,0 +1,13 @@
package responses
type CreateInvoiceConfigResponse struct {
ID int64 `json:"id"`
}
type UpdateInvoiceConfigResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
IsActive bool `json:"isActive"`
}

View File

@@ -0,0 +1,52 @@
package repositories
import (
"context"
"wm-backend/internal/mapper"
"wm-backend/internal/models"
db "wm-backend/sqlc_gen"
)
func CreateInvoiceConfig(ctx context.Context, queries *db.Queries, body models.InvoiceConfig) (models.InvoiceConfig, error) {
result, err := queries.CreateInvoiceConfig(ctx, *mapper.ToModelInvoiceConfig(&body))
if err != nil {
return models.InvoiceConfig{}, err
}
return *mapper.ToDomainInvoiceConfig(result), nil
}
func GetInvoiceConfigByID(ctx context.Context, queries *db.Queries, id int64) (models.InvoiceConfig, error) {
result, err := queries.GetInvoiceConfigByID(ctx, id)
if err != nil {
return models.InvoiceConfig{}, err
}
return *mapper.ToDomainInvoiceConfig(result), nil
}
func ListInvoiceConfigs(ctx context.Context, queries *db.Queries) ([]models.InvoiceConfig, error) {
results, err := queries.ListInvoiceConfigs(ctx)
if err != nil {
return nil, err
}
var items []models.InvoiceConfig
for _, r := range results {
items = append(items, *mapper.ToDomainInvoiceConfig(r))
}
return items, nil
}
func UpdateInvoiceConfig(ctx context.Context, queries *db.Queries, body models.InvoiceConfig) (models.InvoiceConfig, error) {
result, err := queries.UpdateInvoiceConfig(ctx, *mapper.ToUpdateModelInvoiceConfig(&body))
if err != nil {
return models.InvoiceConfig{}, err
}
return *mapper.ToDomainInvoiceConfig(result), nil
}
func DeleteInvoiceConfig(ctx context.Context, queries *db.Queries, id int64) (int64, error) {
rowsAffected, err := queries.DeleteInvoiceConfig(ctx, id)
if err != nil {
return rowsAffected, err
}
return rowsAffected, nil
}

View File

@@ -111,6 +111,15 @@ func NewRouter() *gin.Engine {
componentItem.PUT("/:id", utils.AsyncHandler(services.ComponentItemUpdate)) componentItem.PUT("/:id", utils.AsyncHandler(services.ComponentItemUpdate))
componentItem.DELETE("/:id", utils.AsyncHandler(services.ComponentItemDelete)) componentItem.DELETE("/:id", utils.AsyncHandler(services.ComponentItemDelete))
} }
invoiceConfig := v1.Group(constants.API_GROUP_INVOICE_CONFIG)
{
invoiceConfig.GET("", utils.AsyncHandler(services.InvoiceConfigList))
invoiceConfig.GET("/:id", utils.AsyncHandler(services.InvoiceConfigGetByID))
invoiceConfig.POST("", utils.AsyncHandler(services.InvoiceConfigCreate))
invoiceConfig.PUT("/:id", utils.AsyncHandler(services.InvoiceConfigUpdate))
invoiceConfig.DELETE("/:id", utils.AsyncHandler(services.InvoiceConfigDelete))
}
} }
r.GET(constants.API_PATH_PING, services.PingHandler) r.GET(constants.API_PATH_PING, services.PingHandler)

View File

@@ -0,0 +1,192 @@
package services
import (
"net/http"
"strconv"
"time"
"wm-backend/global"
"wm-backend/internal/models"
"wm-backend/internal/models/requests"
"wm-backend/internal/models/responses"
"wm-backend/internal/repositories"
"wm-backend/pkg/helper"
"wm-backend/response"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
)
// InvoiceConfigCreate creates a new invoice config.
//
// @Summary Create a new invoice config
// @Description Create a new invoice config with the provided details
// @Tags invoice-config
// @Accept json
// @Produce json
// @Param body body requests.CreateInvoiceConfigRequest true "Invoice config request body"
// @Success 201 {object} response.SuccessResponse{data=responses.CreateInvoiceConfigResponse}
// @Failure 400 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /v1/invoice-configs [post]
func InvoiceConfigCreate(c *gin.Context) error {
requestBody := requests.CreateInvoiceConfigRequest{}
if helper.IsShouldBindJSON(c, &requestBody) {
return nil
}
invoiceConfigModel := &models.InvoiceConfig{
Name: requestBody.Name,
Type: requestBody.Type,
Description: requestBody.Description,
IsActive: requestBody.IsActive,
CreatedAt: time.Now(),
}
invoiceConfig, err := repositories.CreateInvoiceConfig(c.Request.Context(), global.Queries, *invoiceConfigModel)
if err != nil {
log.Error().Err(err).Msg("Failed to create invoice config")
response.InternalServerError(c, http.StatusInternalServerError, "Failed to create invoice config")
return nil
}
response.Created(c, "Invoice config created successfully", &responses.CreateInvoiceConfigResponse{
ID: invoiceConfig.ID,
})
return nil
}
// InvoiceConfigGetByID retrieves a single invoice config by its ID.
//
// @Summary Get invoice config by ID
// @Description Retrieve a single invoice config using its unique identifier
// @Tags invoice-config
// @Accept json
// @Produce json
// @Param id path int true "Invoice config ID"
// @Success 200 {object} response.SuccessResponse{data=models.InvoiceConfig}
// @Failure 400 {object} response.ErrorResponse
// @Failure 404 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /v1/invoice-configs/{id} [get]
func InvoiceConfigGetByID(c *gin.Context) error {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequestError(c, http.StatusBadRequest, "Invalid ID")
return nil
}
invoiceConfig, err := repositories.GetInvoiceConfigByID(c.Request.Context(), global.Queries, id)
if err != nil {
log.Error().Err(err).Msgf("Failed to get invoice config by ID: %d", id)
response.NotFoundError(c, http.StatusNotFound, "Invoice config not found")
return nil
}
response.Ok(c, "Success", invoiceConfig)
return nil
}
// InvoiceConfigList retrieves all invoice configs.
//
// @Summary List all invoice configs
// @Description Retrieve a list of all invoice configs ordered by creation date
// @Tags invoice-config
// @Accept json
// @Produce json
// @Success 200 {object} response.SuccessResponse{data=[]models.InvoiceConfig}
// @Failure 500 {object} response.ErrorResponse
// @Router /v1/invoice-configs [get]
func InvoiceConfigList(c *gin.Context) error {
invoiceConfigs, err := repositories.ListInvoiceConfigs(c.Request.Context(), global.Queries)
if err != nil {
response.InternalServerError(c, http.StatusInternalServerError, "Failed to list invoice configs")
return nil
}
response.Ok(c, "Success", invoiceConfigs)
return nil
}
// InvoiceConfigUpdate updates an existing invoice config by its ID.
//
// @Summary Update invoice config
// @Description Update an existing invoice config by its ID. Only non-empty fields will be updated.
// @Tags invoice-config
// @Accept json
// @Produce json
// @Param id path int true "Invoice config ID"
// @Param body body requests.UpdateInvoiceConfigRequest true "Invoice config request body"
// @Success 200 {object} response.SuccessResponse{data=responses.UpdateInvoiceConfigResponse}
// @Failure 400 {object} response.ErrorResponse
// @Failure 404 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /v1/invoice-configs/{id} [put]
func InvoiceConfigUpdate(c *gin.Context) error {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequestError(c, http.StatusBadRequest, "Invalid ID")
return nil
}
requestBody := requests.UpdateInvoiceConfigRequest{}
if helper.IsShouldBindJSON(c, &requestBody) {
return nil
}
existing, err := repositories.GetInvoiceConfigByID(c.Request.Context(), global.Queries, id)
if err != nil {
response.NotFoundError(c, http.StatusNotFound, "Invoice config not found")
return nil
}
if requestBody.Name != "" {
existing.Name = requestBody.Name
}
if requestBody.Type != "" {
existing.Type = requestBody.Type
}
if requestBody.Description != "" {
existing.Description = requestBody.Description
}
if requestBody.IsActive != nil {
existing.IsActive = *requestBody.IsActive
}
existing.UpdatedAt = time.Now()
invoiceConfig, err := repositories.UpdateInvoiceConfig(c.Request.Context(), global.Queries, existing)
if err != nil {
log.Error().Err(err).Msgf("Failed to update invoice config with ID: %d", id)
response.InternalServerError(c, http.StatusInternalServerError, "Failed to update invoice config")
return nil
}
response.Ok(c, "Invoice config updated successfully", &responses.UpdateInvoiceConfigResponse{
ID: invoiceConfig.ID,
Name: invoiceConfig.Name,
Type: invoiceConfig.Type,
Description: invoiceConfig.Description,
IsActive: invoiceConfig.IsActive,
})
return nil
}
// InvoiceConfigDelete deletes an invoice config by its ID.
//
// @Summary Delete invoice config
// @Description Delete an invoice config by its unique identifier
// @Tags invoice-config
// @Accept json
// @Produce json
// @Param id path int true "Invoice config ID"
// @Success 200 {object} response.SuccessResponse
// @Failure 400 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /v1/invoice-configs/{id} [delete]
func InvoiceConfigDelete(c *gin.Context) error {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequestError(c, http.StatusBadRequest, "Invalid ID")
return nil
}
rowsAffected, err := repositories.DeleteInvoiceConfig(c.Request.Context(), global.Queries, id)
if err != nil {
log.Error().Err(err).Msgf("Failed to delete invoice config with ID: %d", id)
response.InternalServerError(c, http.StatusInternalServerError, "Failed to delete invoice config")
return nil
}
if rowsAffected == 0 {
response.NotFoundError(c, http.StatusNotFound, "Invoice config not found")
return nil
}
response.Ok(c, "Delete Success", nil)
return nil
}

View File

@@ -0,0 +1,172 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: invoice_config.sql
package db
import (
"context"
"time"
"github.com/jackc/pgx/v5/pgtype"
)
const createInvoiceConfig = `-- name: CreateInvoiceConfig :one
INSERT INTO invoice_configs (name, type, description, is_active, metadata, created_at)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6
)
RETURNING id, name, type, description, is_active, metadata, created_at, updated_at
`
type CreateInvoiceConfigParams struct {
Name string `db:"name" json:"name"`
Type InvoiceTypeEnum `db:"type" json:"type"`
Description pgtype.Text `db:"description" json:"description"`
IsActive bool `db:"is_active" json:"isActive"`
Metadata []byte `db:"metadata" json:"metadata"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
}
func (q *Queries) CreateInvoiceConfig(ctx context.Context, arg CreateInvoiceConfigParams) (InvoiceConfig, error) {
row := q.db.QueryRow(ctx, createInvoiceConfig,
arg.Name,
arg.Type,
arg.Description,
arg.IsActive,
arg.Metadata,
arg.CreatedAt,
)
var i InvoiceConfig
err := row.Scan(
&i.ID,
&i.Name,
&i.Type,
&i.Description,
&i.IsActive,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const deleteInvoiceConfig = `-- name: DeleteInvoiceConfig :execrows
DELETE FROM invoice_configs
WHERE id = $1
`
func (q *Queries) DeleteInvoiceConfig(ctx context.Context, id int64) (int64, error) {
result, err := q.db.Exec(ctx, deleteInvoiceConfig, id)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const getInvoiceConfigByID = `-- name: GetInvoiceConfigByID :one
SELECT id, name, type, description, is_active, metadata, created_at, updated_at FROM invoice_configs
WHERE id = $1
`
func (q *Queries) GetInvoiceConfigByID(ctx context.Context, id int64) (InvoiceConfig, error) {
row := q.db.QueryRow(ctx, getInvoiceConfigByID, id)
var i InvoiceConfig
err := row.Scan(
&i.ID,
&i.Name,
&i.Type,
&i.Description,
&i.IsActive,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listInvoiceConfigs = `-- name: ListInvoiceConfigs :many
SELECT id, name, type, description, is_active, metadata, created_at, updated_at FROM invoice_configs
ORDER BY created_at DESC
`
func (q *Queries) ListInvoiceConfigs(ctx context.Context) ([]InvoiceConfig, error) {
rows, err := q.db.Query(ctx, listInvoiceConfigs)
if err != nil {
return nil, err
}
defer rows.Close()
var items []InvoiceConfig
for rows.Next() {
var i InvoiceConfig
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Type,
&i.Description,
&i.IsActive,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateInvoiceConfig = `-- name: UpdateInvoiceConfig :one
UPDATE invoice_configs
SET name = CASE WHEN $1 = '' THEN name ELSE $1 END,
type = coalesce($2, type),
description = coalesce($3, description),
is_active = coalesce($4, is_active),
metadata = coalesce($5, metadata),
updated_at = $6
WHERE id = $7
RETURNING id, name, type, description, is_active, metadata, created_at, updated_at
`
type UpdateInvoiceConfigParams struct {
Name interface{} `db:"name" json:"name"`
Type InvoiceTypeEnum `db:"type" json:"type"`
Description pgtype.Text `db:"description" json:"description"`
IsActive bool `db:"is_active" json:"isActive"`
Metadata []byte `db:"metadata" json:"metadata"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
ID int64 `db:"id" json:"id"`
}
func (q *Queries) UpdateInvoiceConfig(ctx context.Context, arg UpdateInvoiceConfigParams) (InvoiceConfig, error) {
row := q.db.QueryRow(ctx, updateInvoiceConfig,
arg.Name,
arg.Type,
arg.Description,
arg.IsActive,
arg.Metadata,
arg.UpdatedAt,
arg.ID,
)
var i InvoiceConfig
err := row.Scan(
&i.ID,
&i.Name,
&i.Type,
&i.Description,
&i.IsActive,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

View File

@@ -20,6 +20,7 @@ type Querier interface {
CreateComponentStatusHistory(ctx context.Context, arg CreateComponentStatusHistoryParams) (ComponentStatusHistory, error) CreateComponentStatusHistory(ctx context.Context, arg CreateComponentStatusHistoryParams) (ComponentStatusHistory, error)
CreateComponentType(ctx context.Context, arg CreateComponentTypeParams) (ComponentType, error) CreateComponentType(ctx context.Context, arg CreateComponentTypeParams) (ComponentType, error)
CreateContainer(ctx context.Context, arg CreateContainerParams) (Container, error) CreateContainer(ctx context.Context, arg CreateContainerParams) (Container, error)
CreateInvoiceConfig(ctx context.Context, arg CreateInvoiceConfigParams) (InvoiceConfig, error)
CreateRole(ctx context.Context, arg CreateRoleParams) (Role, error) CreateRole(ctx context.Context, arg CreateRoleParams) (Role, error)
CreateRoom(ctx context.Context, arg CreateRoomParams) (Room, error) CreateRoom(ctx context.Context, arg CreateRoomParams) (Room, error)
CreateShelve(ctx context.Context, arg CreateShelveParams) (Shelf, error) CreateShelve(ctx context.Context, arg CreateShelveParams) (Shelf, error)
@@ -31,6 +32,7 @@ type Querier interface {
DeleteComponentItem(ctx context.Context, id int64) (int64, error) DeleteComponentItem(ctx context.Context, id int64) (int64, error)
DeleteComponentType(ctx context.Context, id int64) (int64, error) DeleteComponentType(ctx context.Context, id int64) (int64, error)
DeleteContainer(ctx context.Context, id int64) (int64, error) DeleteContainer(ctx context.Context, id int64) (int64, error)
DeleteInvoiceConfig(ctx context.Context, id int64) (int64, error)
DeleteRole(ctx context.Context, id uuid.UUID) (int64, error) DeleteRole(ctx context.Context, id uuid.UUID) (int64, error)
DeleteRoom(ctx context.Context, id int64) (int64, error) DeleteRoom(ctx context.Context, id int64) (int64, error)
DeleteShelve(ctx context.Context, id int64) (int64, error) DeleteShelve(ctx context.Context, id int64) (int64, error)
@@ -43,6 +45,7 @@ type Querier interface {
GetComponentItemByID(ctx context.Context, id int64) (ComponentItem, error) GetComponentItemByID(ctx context.Context, id int64) (ComponentItem, error)
GetComponentTypeByID(ctx context.Context, id int64) (ComponentType, error) GetComponentTypeByID(ctx context.Context, id int64) (ComponentType, error)
GetContainerByID(ctx context.Context, id int64) (Container, error) GetContainerByID(ctx context.Context, id int64) (Container, error)
GetInvoiceConfigByID(ctx context.Context, id int64) (InvoiceConfig, error)
GetRoleByID(ctx context.Context, id uuid.UUID) (Role, error) GetRoleByID(ctx context.Context, id uuid.UUID) (Role, error)
GetRoomByID(ctx context.Context, id int64) (Room, error) GetRoomByID(ctx context.Context, id int64) (Room, error)
GetShelveByID(ctx context.Context, id int64) (Shelf, error) GetShelveByID(ctx context.Context, id int64) (Shelf, error)
@@ -59,6 +62,7 @@ type Querier interface {
ListComponentTypes(ctx context.Context) ([]ComponentType, error) ListComponentTypes(ctx context.Context) ([]ComponentType, error)
ListComponents(ctx context.Context) ([]Component, error) ListComponents(ctx context.Context) ([]Component, error)
ListContainers(ctx context.Context) ([]Container, error) ListContainers(ctx context.Context) ([]Container, error)
ListInvoiceConfigs(ctx context.Context) ([]InvoiceConfig, error)
ListRoles(ctx context.Context) ([]Role, error) ListRoles(ctx context.Context) ([]Role, error)
ListRooms(ctx context.Context) ([]Room, error) ListRooms(ctx context.Context) ([]Room, error)
ListShelves(ctx context.Context) ([]Shelf, error) ListShelves(ctx context.Context) ([]Shelf, error)
@@ -73,6 +77,7 @@ type Querier interface {
UpdateComponentItemStatus(ctx context.Context, arg UpdateComponentItemStatusParams) (ComponentItem, error) UpdateComponentItemStatus(ctx context.Context, arg UpdateComponentItemStatusParams) (ComponentItem, error)
UpdateComponentType(ctx context.Context, arg UpdateComponentTypeParams) (ComponentType, error) UpdateComponentType(ctx context.Context, arg UpdateComponentTypeParams) (ComponentType, error)
UpdateContainer(ctx context.Context, arg UpdateContainerParams) (Container, error) UpdateContainer(ctx context.Context, arg UpdateContainerParams) (Container, error)
UpdateInvoiceConfig(ctx context.Context, arg UpdateInvoiceConfigParams) (InvoiceConfig, error)
UpdateRole(ctx context.Context, arg UpdateRoleParams) (Role, error) UpdateRole(ctx context.Context, arg UpdateRoleParams) (Role, error)
UpdateRoom(ctx context.Context, arg UpdateRoomParams) (Room, error) UpdateRoom(ctx context.Context, arg UpdateRoomParams) (Room, error)
UpdateShelve(ctx context.Context, arg UpdateShelveParams) (Shelf, error) UpdateShelve(ctx context.Context, arg UpdateShelveParams) (Shelf, error)