diff --git a/configs/constants/constants.go b/configs/constants/constants.go index ba49664..6d4c9ac 100644 --- a/configs/constants/constants.go +++ b/configs/constants/constants.go @@ -19,7 +19,8 @@ const ( API_GROUP_COMPONENT_TYPE = "/component-types" API_GROUP_COMPONENT = "/components" 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 ( diff --git a/db/queries/invoice_config.sql b/db/queries/invoice_config.sql new file mode 100644 index 0000000..c8276f8 --- /dev/null +++ b/db/queries/invoice_config.sql @@ -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); diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index e77948d..4c80cb3 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -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": { "get": { "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": { "type": "object", "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": { "type": "object", "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": { "type": "object", "properties": { @@ -3485,6 +3828,14 @@ const docTemplate = `{ } } }, + "responses.CreateInvoiceConfigResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, "responses.CreateRoomResponse": { "type": "object", "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": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 7a35ce9..8474a01 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -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": { "get": { "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": { "type": "object", "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": { "type": "object", "required": [ @@ -3351,6 +3677,23 @@ } } }, + "requests.UpdateInvoiceConfigRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, "requests.UpdateRoomRequest": { "type": "object", "properties": { @@ -3479,6 +3822,14 @@ } } }, + "responses.CreateInvoiceConfigResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, "responses.CreateRoomResponse": { "type": "object", "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": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 734cbb4..a4e8058 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -143,6 +143,27 @@ definitions: warehouseName: type: string 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: properties: createdAt: @@ -312,6 +333,20 @@ definitions: - name - shelfId type: object + requests.CreateInvoiceConfigRequest: + properties: + description: + type: string + isActive: + type: boolean + name: + type: string + type: + type: string + required: + - name + - type + type: object requests.CreateRoomRequest: properties: description: @@ -444,6 +479,17 @@ definitions: name: type: string type: object + requests.UpdateInvoiceConfigRequest: + properties: + description: + type: string + isActive: + type: boolean + name: + type: string + type: + type: string + type: object requests.UpdateRoomRequest: properties: description: @@ -526,6 +572,11 @@ definitions: id: type: integer type: object + responses.CreateInvoiceConfigResponse: + properties: + id: + type: integer + type: object responses.CreateRoomResponse: properties: id: @@ -642,6 +693,19 @@ definitions: shelfId: type: integer type: object + responses.UpdateInvoiceConfigResponse: + properties: + description: + type: string + id: + type: integer + isActive: + type: boolean + name: + type: string + type: + type: string + type: object responses.UpdateRoomResponse: properties: description: @@ -1844,6 +1908,176 @@ paths: summary: Update container tags: - 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: get: consumes: diff --git a/internal/mapper/invoice_config_mapper.go b/internal/mapper/invoice_config_mapper.go new file mode 100644 index 0000000..43b3a4b --- /dev/null +++ b/internal/mapper/invoice_config_mapper.go @@ -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, + } +} diff --git a/internal/models/invoice_config_model.go b/internal/models/invoice_config_model.go new file mode 100644 index 0000000..8071542 --- /dev/null +++ b/internal/models/invoice_config_model.go @@ -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"` +} diff --git a/internal/models/requests/invoice_config_request.go b/internal/models/requests/invoice_config_request.go new file mode 100644 index 0000000..09d8f70 --- /dev/null +++ b/internal/models/requests/invoice_config_request.go @@ -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"` +} diff --git a/internal/models/responses/invoice_config_response.go b/internal/models/responses/invoice_config_response.go new file mode 100644 index 0000000..9d9cf33 --- /dev/null +++ b/internal/models/responses/invoice_config_response.go @@ -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"` +} diff --git a/internal/repositories/invoice_config_repository.go b/internal/repositories/invoice_config_repository.go new file mode 100644 index 0000000..44089ed --- /dev/null +++ b/internal/repositories/invoice_config_repository.go @@ -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 +} diff --git a/internal/routers/router.go b/internal/routers/router.go index 5221db9..b23e86c 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -111,6 +111,15 @@ func NewRouter() *gin.Engine { componentItem.PUT("/:id", utils.AsyncHandler(services.ComponentItemUpdate)) 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) diff --git a/internal/services/invoice_config_service.go b/internal/services/invoice_config_service.go new file mode 100644 index 0000000..8117084 --- /dev/null +++ b/internal/services/invoice_config_service.go @@ -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 +} diff --git a/sqlc_gen/invoice_config.sql.go b/sqlc_gen/invoice_config.sql.go new file mode 100644 index 0000000..55daf9b --- /dev/null +++ b/sqlc_gen/invoice_config.sql.go @@ -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 +} diff --git a/sqlc_gen/querier.go b/sqlc_gen/querier.go index db53930..f9a6346 100644 --- a/sqlc_gen/querier.go +++ b/sqlc_gen/querier.go @@ -20,6 +20,7 @@ type Querier interface { CreateComponentStatusHistory(ctx context.Context, arg CreateComponentStatusHistoryParams) (ComponentStatusHistory, error) CreateComponentType(ctx context.Context, arg CreateComponentTypeParams) (ComponentType, 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) CreateRoom(ctx context.Context, arg CreateRoomParams) (Room, error) CreateShelve(ctx context.Context, arg CreateShelveParams) (Shelf, error) @@ -31,6 +32,7 @@ type Querier interface { DeleteComponentItem(ctx context.Context, id int64) (int64, error) DeleteComponentType(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) DeleteRoom(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) GetComponentTypeByID(ctx context.Context, id int64) (ComponentType, 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) GetRoomByID(ctx context.Context, id int64) (Room, error) GetShelveByID(ctx context.Context, id int64) (Shelf, error) @@ -59,6 +62,7 @@ type Querier interface { ListComponentTypes(ctx context.Context) ([]ComponentType, error) ListComponents(ctx context.Context) ([]Component, error) ListContainers(ctx context.Context) ([]Container, error) + ListInvoiceConfigs(ctx context.Context) ([]InvoiceConfig, error) ListRoles(ctx context.Context) ([]Role, error) ListRooms(ctx context.Context) ([]Room, error) ListShelves(ctx context.Context) ([]Shelf, error) @@ -73,6 +77,7 @@ type Querier interface { UpdateComponentItemStatus(ctx context.Context, arg UpdateComponentItemStatusParams) (ComponentItem, error) UpdateComponentType(ctx context.Context, arg UpdateComponentTypeParams) (ComponentType, 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) UpdateRoom(ctx context.Context, arg UpdateRoomParams) (Room, error) UpdateShelve(ctx context.Context, arg UpdateShelveParams) (Shelf, error)