diff --git a/configs/constants/constants.go b/configs/constants/constants.go index 6d4c9ac..887a951 100644 --- a/configs/constants/constants.go +++ b/configs/constants/constants.go @@ -21,6 +21,7 @@ const ( API_GROUP_COMPONENT_CODE = "/component-codes" API_GROUP_COMPONENT_ITEM = "/component-items" API_GROUP_INVOICE_CONFIG = "/invoice-configs" + API_GROUP_INVOICE_CONFIG_ITEM = "/invoice-config-items" ) const ( diff --git a/db/queries/invoice_config_item.sql b/db/queries/invoice_config_item.sql new file mode 100644 index 0000000..7c76e21 --- /dev/null +++ b/db/queries/invoice_config_item.sql @@ -0,0 +1,33 @@ +-- name: GetInvoiceConfigItemByID :one +SELECT * FROM invoice_config_items +WHERE id = sqlc.arg(id); + +-- name: ListInvoiceConfigItems :many +SELECT * FROM invoice_config_items; + +-- name: CreateInvoiceConfigItem :one +INSERT INTO invoice_config_items (invoice_config_id,component_id,required_quantity, allow_alternative,priority_order, note, metadata) +VALUES ( + sqlc.arg(invoice_config_id), + sqlc.arg(component_id), + sqlc.arg(required_quantity), + sqlc.arg(allow_alternative), + sqlc.arg(priority_order), + sqlc.arg(note), + sqlc.arg(metadata) +) +RETURNING *; + +-- name: UpdateInvoiceConfigItem :one +UPDATE invoice_config_items +SET required_quantity = coalesce(sqlc.arg(required_quantity), required_quantity), + allow_alternative = coalesce(sqlc.arg(allow_alternative), allow_alternative), + priority_order = coalesce(sqlc.arg(priority_order), priority_order), + note = coalesce(sqlc.arg(note), note), + metadata = coalesce(sqlc.arg(metadata), metadata) +WHERE id = sqlc.arg(id) +RETURNING *; + +-- name: DeleteInvoiceConfigItem :execrows +DELETE FROM invoice_config_items +WHERE id = sqlc.arg(id); diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 4c80cb3..302d791 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -1873,6 +1873,279 @@ const docTemplate = `{ } } }, + "/v1/invoice-config-items": { + "get": { + "description": "Retrieve a list of all invoice config items", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "List all invoice config items", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.InvoiceConfigItem" + } + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new invoice config item with the provided details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Create a new invoice config item", + "parameters": [ + { + "description": "Invoice config item request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CreateInvoiceConfigItemRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CreateInvoiceConfigItemResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/invoice-config-items/{id}": { + "get": { + "description": "Retrieve a single invoice config item using its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Get invoice config item by ID", + "parameters": [ + { + "type": "integer", + "description": "Invoice config item ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.InvoiceConfigItem" + } + } + } + ] + } + }, + "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 item by its ID. Only non-empty fields will be updated.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Update invoice config item", + "parameters": [ + { + "type": "integer", + "description": "Invoice config item ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Invoice config item request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UpdateInvoiceConfigItemRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.UpdateInvoiceConfigItemResponse" + } + } + } + ] + } + }, + "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 item by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Delete invoice config item", + "parameters": [ + { + "type": "integer", + "description": "Invoice config item 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/invoice-configs": { "get": { "description": "Retrieve a list of all invoice configs ordered by creation date", @@ -3211,6 +3484,38 @@ const docTemplate = `{ } } }, + "models.InvoiceConfigItem": { + "type": "object", + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "componentId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "invoiceConfigId": { + "type": "integer" + }, + "metadata": { + "type": "array", + "items": { + "type": "integer" + } + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "models.Room": { "type": "object", "properties": { @@ -3463,6 +3768,35 @@ const docTemplate = `{ } } }, + "requests.CreateInvoiceConfigItemRequest": { + "type": "object", + "required": [ + "componentId", + "invoiceConfigId", + "priorityOrder", + "requiredQuantity" + ], + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "componentId": { + "type": "integer" + }, + "invoiceConfigId": { + "type": "integer" + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "requests.CreateInvoiceConfigRequest": { "type": "object", "required": [ @@ -3683,6 +4017,23 @@ const docTemplate = `{ } } }, + "requests.UpdateInvoiceConfigItemRequest": { + "type": "object", + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "requests.UpdateInvoiceConfigRequest": { "type": "object", "properties": { @@ -3828,6 +4179,14 @@ const docTemplate = `{ } } }, + "responses.CreateInvoiceConfigItemResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, "responses.CreateInvoiceConfigResponse": { "type": "object", "properties": { @@ -4015,6 +4374,32 @@ const docTemplate = `{ } } }, + "responses.UpdateInvoiceConfigItemResponse": { + "type": "object", + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "componentId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "invoiceConfigId": { + "type": "integer" + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "responses.UpdateInvoiceConfigResponse": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 8474a01..b54f038 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -1867,6 +1867,279 @@ } } }, + "/v1/invoice-config-items": { + "get": { + "description": "Retrieve a list of all invoice config items", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "List all invoice config items", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.InvoiceConfigItem" + } + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new invoice config item with the provided details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Create a new invoice config item", + "parameters": [ + { + "description": "Invoice config item request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CreateInvoiceConfigItemRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CreateInvoiceConfigItemResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/invoice-config-items/{id}": { + "get": { + "description": "Retrieve a single invoice config item using its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Get invoice config item by ID", + "parameters": [ + { + "type": "integer", + "description": "Invoice config item ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.InvoiceConfigItem" + } + } + } + ] + } + }, + "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 item by its ID. Only non-empty fields will be updated.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Update invoice config item", + "parameters": [ + { + "type": "integer", + "description": "Invoice config item ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Invoice config item request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UpdateInvoiceConfigItemRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.UpdateInvoiceConfigItemResponse" + } + } + } + ] + } + }, + "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 item by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "invoice-config-item" + ], + "summary": "Delete invoice config item", + "parameters": [ + { + "type": "integer", + "description": "Invoice config item 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/invoice-configs": { "get": { "description": "Retrieve a list of all invoice configs ordered by creation date", @@ -3205,6 +3478,38 @@ } } }, + "models.InvoiceConfigItem": { + "type": "object", + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "componentId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "invoiceConfigId": { + "type": "integer" + }, + "metadata": { + "type": "array", + "items": { + "type": "integer" + } + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "models.Room": { "type": "object", "properties": { @@ -3457,6 +3762,35 @@ } } }, + "requests.CreateInvoiceConfigItemRequest": { + "type": "object", + "required": [ + "componentId", + "invoiceConfigId", + "priorityOrder", + "requiredQuantity" + ], + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "componentId": { + "type": "integer" + }, + "invoiceConfigId": { + "type": "integer" + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "requests.CreateInvoiceConfigRequest": { "type": "object", "required": [ @@ -3677,6 +4011,23 @@ } } }, + "requests.UpdateInvoiceConfigItemRequest": { + "type": "object", + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "requests.UpdateInvoiceConfigRequest": { "type": "object", "properties": { @@ -3822,6 +4173,14 @@ } } }, + "responses.CreateInvoiceConfigItemResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, "responses.CreateInvoiceConfigResponse": { "type": "object", "properties": { @@ -4009,6 +4368,32 @@ } } }, + "responses.UpdateInvoiceConfigItemResponse": { + "type": "object", + "properties": { + "allowAlternative": { + "type": "boolean" + }, + "componentId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "invoiceConfigId": { + "type": "integer" + }, + "note": { + "type": "string" + }, + "priorityOrder": { + "type": "integer" + }, + "requiredQuantity": { + "type": "integer" + } + } + }, "responses.UpdateInvoiceConfigResponse": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index a4e8058..0c3274d 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -164,6 +164,27 @@ definitions: updatedAt: type: string type: object + models.InvoiceConfigItem: + properties: + allowAlternative: + type: boolean + componentId: + type: integer + id: + type: integer + invoiceConfigId: + type: integer + metadata: + items: + type: integer + type: array + note: + type: string + priorityOrder: + type: integer + requiredQuantity: + type: integer + type: object models.Room: properties: createdAt: @@ -333,6 +354,26 @@ definitions: - name - shelfId type: object + requests.CreateInvoiceConfigItemRequest: + properties: + allowAlternative: + type: boolean + componentId: + type: integer + invoiceConfigId: + type: integer + note: + type: string + priorityOrder: + type: integer + requiredQuantity: + type: integer + required: + - componentId + - invoiceConfigId + - priorityOrder + - requiredQuantity + type: object requests.CreateInvoiceConfigRequest: properties: description: @@ -479,6 +520,17 @@ definitions: name: type: string type: object + requests.UpdateInvoiceConfigItemRequest: + properties: + allowAlternative: + type: boolean + note: + type: string + priorityOrder: + type: integer + requiredQuantity: + type: integer + type: object requests.UpdateInvoiceConfigRequest: properties: description: @@ -572,6 +624,11 @@ definitions: id: type: integer type: object + responses.CreateInvoiceConfigItemResponse: + properties: + id: + type: integer + type: object responses.CreateInvoiceConfigResponse: properties: id: @@ -693,6 +750,23 @@ definitions: shelfId: type: integer type: object + responses.UpdateInvoiceConfigItemResponse: + properties: + allowAlternative: + type: boolean + componentId: + type: integer + id: + type: integer + invoiceConfigId: + type: integer + note: + type: string + priorityOrder: + type: integer + requiredQuantity: + type: integer + type: object responses.UpdateInvoiceConfigResponse: properties: description: @@ -1908,6 +1982,176 @@ paths: summary: Update container tags: - container + /v1/invoice-config-items: + get: + consumes: + - application/json + description: Retrieve a list of all invoice config items + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.SuccessResponse' + - properties: + data: + items: + $ref: '#/definitions/models.InvoiceConfigItem' + type: array + type: object + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.ErrorResponse' + summary: List all invoice config items + tags: + - invoice-config-item + post: + consumes: + - application/json + description: Create a new invoice config item with the provided details + parameters: + - description: Invoice config item request body + in: body + name: body + required: true + schema: + $ref: '#/definitions/requests.CreateInvoiceConfigItemRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + allOf: + - $ref: '#/definitions/response.SuccessResponse' + - properties: + data: + $ref: '#/definitions/responses.CreateInvoiceConfigItemResponse' + 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 item + tags: + - invoice-config-item + /v1/invoice-config-items/{id}: + delete: + consumes: + - application/json + description: Delete an invoice config item by its unique identifier + parameters: + - description: Invoice config item 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 item + tags: + - invoice-config-item + get: + consumes: + - application/json + description: Retrieve a single invoice config item using its unique identifier + parameters: + - description: Invoice config item 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.InvoiceConfigItem' + 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 item by ID + tags: + - invoice-config-item + put: + consumes: + - application/json + description: Update an existing invoice config item by its ID. Only non-empty + fields will be updated. + parameters: + - description: Invoice config item ID + in: path + name: id + required: true + type: integer + - description: Invoice config item request body + in: body + name: body + required: true + schema: + $ref: '#/definitions/requests.UpdateInvoiceConfigItemRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.SuccessResponse' + - properties: + data: + $ref: '#/definitions/responses.UpdateInvoiceConfigItemResponse' + 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 item + tags: + - invoice-config-item /v1/invoice-configs: get: consumes: diff --git a/internal/mapper/invoice_config_item_mapper.go b/internal/mapper/invoice_config_item_mapper.go new file mode 100644 index 0000000..409a17e --- /dev/null +++ b/internal/mapper/invoice_config_item_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 ToDomainInvoiceConfigItem(r db.InvoiceConfigItem) *models.InvoiceConfigItem { + return &models.InvoiceConfigItem{ + ID: r.ID, + InvoiceConfigID: r.InvoiceConfigID, + ComponentID: r.ComponentID, + RequiredQuantity: r.RequiredQuantity, + AllowAlternative: r.AllowAlternative, + PriorityOrder: r.PriorityOrder, + Note: r.Note.String, + Metadata: r.Metadata, + } +} + +func ToModelInvoiceConfigItem(r *models.InvoiceConfigItem) *db.CreateInvoiceConfigItemParams { + return &db.CreateInvoiceConfigItemParams{ + InvoiceConfigID: r.InvoiceConfigID, + ComponentID: r.ComponentID, + RequiredQuantity: r.RequiredQuantity, + AllowAlternative: r.AllowAlternative, + PriorityOrder: r.PriorityOrder, + Note: pgtype.Text{ + String: r.Note, + Valid: r.Note != "", + }, + Metadata: r.Metadata, + } +} + +func ToUpdateModelInvoiceConfigItem(r *models.InvoiceConfigItem) *db.UpdateInvoiceConfigItemParams { + return &db.UpdateInvoiceConfigItemParams{ + RequiredQuantity: r.RequiredQuantity, + AllowAlternative: r.AllowAlternative, + PriorityOrder: r.PriorityOrder, + Note: pgtype.Text{ + String: r.Note, + Valid: r.Note != "", + }, + Metadata: r.Metadata, + ID: r.ID, + } +} diff --git a/internal/models/invoice_config_item_model.go b/internal/models/invoice_config_item_model.go new file mode 100644 index 0000000..a566695 --- /dev/null +++ b/internal/models/invoice_config_item_model.go @@ -0,0 +1,12 @@ +package models + +type InvoiceConfigItem struct { + ID int64 `json:"id"` + InvoiceConfigID int64 `json:"invoiceConfigId"` + ComponentID int64 `json:"componentId"` + RequiredQuantity int32 `json:"requiredQuantity"` + AllowAlternative bool `json:"allowAlternative"` + PriorityOrder int32 `json:"priorityOrder"` + Note string `json:"note"` + Metadata []byte `json:"metadata"` +} diff --git a/internal/models/requests/invoice_config_item_request.go b/internal/models/requests/invoice_config_item_request.go new file mode 100644 index 0000000..c63fb4e --- /dev/null +++ b/internal/models/requests/invoice_config_item_request.go @@ -0,0 +1,17 @@ +package requests + +type CreateInvoiceConfigItemRequest struct { + InvoiceConfigID int64 `json:"invoiceConfigId" binding:"required"` + ComponentID int64 `json:"componentId" binding:"required"` + RequiredQuantity int32 `json:"requiredQuantity" binding:"required"` + AllowAlternative bool `json:"allowAlternative"` + PriorityOrder int32 `json:"priorityOrder" binding:"required"` + Note string `json:"note"` +} + +type UpdateInvoiceConfigItemRequest struct { + RequiredQuantity *int32 `json:"requiredQuantity"` + AllowAlternative *bool `json:"allowAlternative"` + PriorityOrder *int32 `json:"priorityOrder"` + Note string `json:"note"` +} diff --git a/internal/models/responses/invoice_config_item_response.go b/internal/models/responses/invoice_config_item_response.go new file mode 100644 index 0000000..fe7148e --- /dev/null +++ b/internal/models/responses/invoice_config_item_response.go @@ -0,0 +1,15 @@ +package responses + +type CreateInvoiceConfigItemResponse struct { + ID int64 `json:"id"` +} + +type UpdateInvoiceConfigItemResponse struct { + ID int64 `json:"id"` + InvoiceConfigID int64 `json:"invoiceConfigId"` + ComponentID int64 `json:"componentId"` + RequiredQuantity int32 `json:"requiredQuantity"` + AllowAlternative bool `json:"allowAlternative"` + PriorityOrder int32 `json:"priorityOrder"` + Note string `json:"note"` +} diff --git a/internal/repositories/invoice_config_item_repository.go b/internal/repositories/invoice_config_item_repository.go new file mode 100644 index 0000000..ab8b5ab --- /dev/null +++ b/internal/repositories/invoice_config_item_repository.go @@ -0,0 +1,52 @@ +package repositories + +import ( + "context" + "wm-backend/internal/mapper" + "wm-backend/internal/models" + db "wm-backend/sqlc_gen" +) + +func CreateInvoiceConfigItem(ctx context.Context, queries *db.Queries, body models.InvoiceConfigItem) (models.InvoiceConfigItem, error) { + result, err := queries.CreateInvoiceConfigItem(ctx, *mapper.ToModelInvoiceConfigItem(&body)) + if err != nil { + return models.InvoiceConfigItem{}, err + } + return *mapper.ToDomainInvoiceConfigItem(result), nil +} + +func GetInvoiceConfigItemByID(ctx context.Context, queries *db.Queries, id int64) (models.InvoiceConfigItem, error) { + result, err := queries.GetInvoiceConfigItemByID(ctx, id) + if err != nil { + return models.InvoiceConfigItem{}, err + } + return *mapper.ToDomainInvoiceConfigItem(result), nil +} + +func ListInvoiceConfigItems(ctx context.Context, queries *db.Queries) ([]models.InvoiceConfigItem, error) { + results, err := queries.ListInvoiceConfigItems(ctx) + if err != nil { + return nil, err + } + var items []models.InvoiceConfigItem + for _, r := range results { + items = append(items, *mapper.ToDomainInvoiceConfigItem(r)) + } + return items, nil +} + +func UpdateInvoiceConfigItem(ctx context.Context, queries *db.Queries, body models.InvoiceConfigItem) (models.InvoiceConfigItem, error) { + result, err := queries.UpdateInvoiceConfigItem(ctx, *mapper.ToUpdateModelInvoiceConfigItem(&body)) + if err != nil { + return models.InvoiceConfigItem{}, err + } + return *mapper.ToDomainInvoiceConfigItem(result), nil +} + +func DeleteInvoiceConfigItem(ctx context.Context, queries *db.Queries, id int64) (int64, error) { + rowsAffected, err := queries.DeleteInvoiceConfigItem(ctx, id) + if err != nil { + return rowsAffected, err + } + return rowsAffected, nil +} diff --git a/internal/routers/router.go b/internal/routers/router.go index b23e86c..f27625d 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -120,6 +120,15 @@ func NewRouter() *gin.Engine { invoiceConfig.PUT("/:id", utils.AsyncHandler(services.InvoiceConfigUpdate)) invoiceConfig.DELETE("/:id", utils.AsyncHandler(services.InvoiceConfigDelete)) } + + invoiceConfigItem := v1.Group(constants.API_GROUP_INVOICE_CONFIG_ITEM) + { + invoiceConfigItem.GET("", utils.AsyncHandler(services.InvoiceConfigItemList)) + invoiceConfigItem.GET("/:id", utils.AsyncHandler(services.InvoiceConfigItemGetByID)) + invoiceConfigItem.POST("", utils.AsyncHandler(services.InvoiceConfigItemCreate)) + invoiceConfigItem.PUT("/:id", utils.AsyncHandler(services.InvoiceConfigItemUpdate)) + invoiceConfigItem.DELETE("/:id", utils.AsyncHandler(services.InvoiceConfigItemDelete)) + } } r.GET(constants.API_PATH_PING, services.PingHandler) diff --git a/internal/services/invoice_config_item_service.go b/internal/services/invoice_config_item_service.go new file mode 100644 index 0000000..520a2ad --- /dev/null +++ b/internal/services/invoice_config_item_service.go @@ -0,0 +1,193 @@ +package services + +import ( + "net/http" + "strconv" + "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" +) + +// InvoiceConfigItemCreate creates a new invoice config item. +// +// @Summary Create a new invoice config item +// @Description Create a new invoice config item with the provided details +// @Tags invoice-config-item +// @Accept json +// @Produce json +// @Param body body requests.CreateInvoiceConfigItemRequest true "Invoice config item request body" +// @Success 201 {object} response.SuccessResponse{data=responses.CreateInvoiceConfigItemResponse} +// @Failure 400 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/invoice-config-items [post] +func InvoiceConfigItemCreate(c *gin.Context) error { + requestBody := requests.CreateInvoiceConfigItemRequest{} + if helper.IsShouldBindJSON(c, &requestBody) { + return nil + } + invoiceConfigItemModel := &models.InvoiceConfigItem{ + InvoiceConfigID: requestBody.InvoiceConfigID, + ComponentID: requestBody.ComponentID, + RequiredQuantity: requestBody.RequiredQuantity, + AllowAlternative: requestBody.AllowAlternative, + PriorityOrder: requestBody.PriorityOrder, + Note: requestBody.Note, + } + invoiceConfigItem, err := repositories.CreateInvoiceConfigItem(c.Request.Context(), global.Queries, *invoiceConfigItemModel) + if err != nil { + log.Error().Err(err).Msg("Failed to create invoice config item") + response.InternalServerError(c, http.StatusInternalServerError, "Failed to create invoice config item") + return nil + } + response.Created(c, "Invoice config item created successfully", &responses.CreateInvoiceConfigItemResponse{ + ID: invoiceConfigItem.ID, + }) + return nil +} + +// InvoiceConfigItemGetByID retrieves a single invoice config item by its ID. +// +// @Summary Get invoice config item by ID +// @Description Retrieve a single invoice config item using its unique identifier +// @Tags invoice-config-item +// @Accept json +// @Produce json +// @Param id path int true "Invoice config item ID" +// @Success 200 {object} response.SuccessResponse{data=models.InvoiceConfigItem} +// @Failure 400 {object} response.ErrorResponse +// @Failure 404 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/invoice-config-items/{id} [get] +func InvoiceConfigItemGetByID(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 + } + invoiceConfigItem, err := repositories.GetInvoiceConfigItemByID(c.Request.Context(), global.Queries, id) + if err != nil { + log.Error().Err(err).Msgf("Failed to get invoice config item by ID: %d", id) + response.NotFoundError(c, http.StatusNotFound, "Invoice config item not found") + return nil + } + response.Ok(c, "Success", invoiceConfigItem) + return nil +} + +// InvoiceConfigItemList retrieves all invoice config items. +// +// @Summary List all invoice config items +// @Description Retrieve a list of all invoice config items +// @Tags invoice-config-item +// @Accept json +// @Produce json +// @Success 200 {object} response.SuccessResponse{data=[]models.InvoiceConfigItem} +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/invoice-config-items [get] +func InvoiceConfigItemList(c *gin.Context) error { + invoiceConfigItems, err := repositories.ListInvoiceConfigItems(c.Request.Context(), global.Queries) + if err != nil { + response.InternalServerError(c, http.StatusInternalServerError, "Failed to list invoice config items") + return nil + } + response.Ok(c, "Success", invoiceConfigItems) + return nil +} + +// InvoiceConfigItemUpdate updates an existing invoice config item by its ID. +// +// @Summary Update invoice config item +// @Description Update an existing invoice config item by its ID. Only non-empty fields will be updated. +// @Tags invoice-config-item +// @Accept json +// @Produce json +// @Param id path int true "Invoice config item ID" +// @Param body body requests.UpdateInvoiceConfigItemRequest true "Invoice config item request body" +// @Success 200 {object} response.SuccessResponse{data=responses.UpdateInvoiceConfigItemResponse} +// @Failure 400 {object} response.ErrorResponse +// @Failure 404 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/invoice-config-items/{id} [put] +func InvoiceConfigItemUpdate(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.UpdateInvoiceConfigItemRequest{} + if helper.IsShouldBindJSON(c, &requestBody) { + return nil + } + existing, err := repositories.GetInvoiceConfigItemByID(c.Request.Context(), global.Queries, id) + if err != nil { + response.NotFoundError(c, http.StatusNotFound, "Invoice config item not found") + return nil + } + if requestBody.RequiredQuantity != nil { + existing.RequiredQuantity = *requestBody.RequiredQuantity + } + if requestBody.AllowAlternative != nil { + existing.AllowAlternative = *requestBody.AllowAlternative + } + if requestBody.PriorityOrder != nil { + existing.PriorityOrder = *requestBody.PriorityOrder + } + if requestBody.Note != "" { + existing.Note = requestBody.Note + } + invoiceConfigItem, err := repositories.UpdateInvoiceConfigItem(c.Request.Context(), global.Queries, existing) + if err != nil { + log.Error().Err(err).Msgf("Failed to update invoice config item with ID: %d", id) + response.InternalServerError(c, http.StatusInternalServerError, "Failed to update invoice config item") + return nil + } + response.Ok(c, "Invoice config item updated successfully", &responses.UpdateInvoiceConfigItemResponse{ + ID: invoiceConfigItem.ID, + InvoiceConfigID: invoiceConfigItem.InvoiceConfigID, + ComponentID: invoiceConfigItem.ComponentID, + RequiredQuantity: invoiceConfigItem.RequiredQuantity, + AllowAlternative: invoiceConfigItem.AllowAlternative, + PriorityOrder: invoiceConfigItem.PriorityOrder, + Note: invoiceConfigItem.Note, + }) + return nil +} + +// InvoiceConfigItemDelete deletes an invoice config item by its ID. +// +// @Summary Delete invoice config item +// @Description Delete an invoice config item by its unique identifier +// @Tags invoice-config-item +// @Accept json +// @Produce json +// @Param id path int true "Invoice config item ID" +// @Success 200 {object} response.SuccessResponse +// @Failure 400 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/invoice-config-items/{id} [delete] +func InvoiceConfigItemDelete(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.DeleteInvoiceConfigItem(c.Request.Context(), global.Queries, id) + if err != nil { + log.Error().Err(err).Msgf("Failed to delete invoice config item with ID: %d", id) + response.InternalServerError(c, http.StatusInternalServerError, "Failed to delete invoice config item") + return nil + } + if rowsAffected == 0 { + response.NotFoundError(c, http.StatusNotFound, "Invoice config item not found") + return nil + } + response.Ok(c, "Delete Success", nil) + return nil +} diff --git a/sqlc_gen/invoice_config_item.sql.go b/sqlc_gen/invoice_config_item.sql.go new file mode 100644 index 0000000..c5aa53a --- /dev/null +++ b/sqlc_gen/invoice_config_item.sql.go @@ -0,0 +1,170 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: invoice_config_item.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createInvoiceConfigItem = `-- name: CreateInvoiceConfigItem :one +INSERT INTO invoice_config_items (invoice_config_id,component_id,required_quantity, allow_alternative,priority_order, note, metadata) +VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7 +) +RETURNING id, invoice_config_id, component_id, required_quantity, allow_alternative, priority_order, note, metadata +` + +type CreateInvoiceConfigItemParams struct { + InvoiceConfigID int64 `db:"invoice_config_id" json:"invoiceConfigId"` + ComponentID int64 `db:"component_id" json:"componentId"` + RequiredQuantity int32 `db:"required_quantity" json:"requiredQuantity"` + AllowAlternative bool `db:"allow_alternative" json:"allowAlternative"` + PriorityOrder int32 `db:"priority_order" json:"priorityOrder"` + Note pgtype.Text `db:"note" json:"note"` + Metadata []byte `db:"metadata" json:"metadata"` +} + +func (q *Queries) CreateInvoiceConfigItem(ctx context.Context, arg CreateInvoiceConfigItemParams) (InvoiceConfigItem, error) { + row := q.db.QueryRow(ctx, createInvoiceConfigItem, + arg.InvoiceConfigID, + arg.ComponentID, + arg.RequiredQuantity, + arg.AllowAlternative, + arg.PriorityOrder, + arg.Note, + arg.Metadata, + ) + var i InvoiceConfigItem + err := row.Scan( + &i.ID, + &i.InvoiceConfigID, + &i.ComponentID, + &i.RequiredQuantity, + &i.AllowAlternative, + &i.PriorityOrder, + &i.Note, + &i.Metadata, + ) + return i, err +} + +const deleteInvoiceConfigItem = `-- name: DeleteInvoiceConfigItem :execrows +DELETE FROM invoice_config_items +WHERE id = $1 +` + +func (q *Queries) DeleteInvoiceConfigItem(ctx context.Context, id int64) (int64, error) { + result, err := q.db.Exec(ctx, deleteInvoiceConfigItem, id) + if err != nil { + return 0, err + } + return result.RowsAffected(), nil +} + +const getInvoiceConfigItemByID = `-- name: GetInvoiceConfigItemByID :one +SELECT id, invoice_config_id, component_id, required_quantity, allow_alternative, priority_order, note, metadata FROM invoice_config_items +WHERE id = $1 +` + +func (q *Queries) GetInvoiceConfigItemByID(ctx context.Context, id int64) (InvoiceConfigItem, error) { + row := q.db.QueryRow(ctx, getInvoiceConfigItemByID, id) + var i InvoiceConfigItem + err := row.Scan( + &i.ID, + &i.InvoiceConfigID, + &i.ComponentID, + &i.RequiredQuantity, + &i.AllowAlternative, + &i.PriorityOrder, + &i.Note, + &i.Metadata, + ) + return i, err +} + +const listInvoiceConfigItems = `-- name: ListInvoiceConfigItems :many +SELECT id, invoice_config_id, component_id, required_quantity, allow_alternative, priority_order, note, metadata FROM invoice_config_items +` + +func (q *Queries) ListInvoiceConfigItems(ctx context.Context) ([]InvoiceConfigItem, error) { + rows, err := q.db.Query(ctx, listInvoiceConfigItems) + if err != nil { + return nil, err + } + defer rows.Close() + var items []InvoiceConfigItem + for rows.Next() { + var i InvoiceConfigItem + if err := rows.Scan( + &i.ID, + &i.InvoiceConfigID, + &i.ComponentID, + &i.RequiredQuantity, + &i.AllowAlternative, + &i.PriorityOrder, + &i.Note, + &i.Metadata, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateInvoiceConfigItem = `-- name: UpdateInvoiceConfigItem :one +UPDATE invoice_config_items +SET required_quantity = coalesce($1, required_quantity), + allow_alternative = coalesce($2, allow_alternative), + priority_order = coalesce($3, priority_order), + note = coalesce($4, note), + metadata = coalesce($5, metadata) +WHERE id = $6 +RETURNING id, invoice_config_id, component_id, required_quantity, allow_alternative, priority_order, note, metadata +` + +type UpdateInvoiceConfigItemParams struct { + RequiredQuantity int32 `db:"required_quantity" json:"requiredQuantity"` + AllowAlternative bool `db:"allow_alternative" json:"allowAlternative"` + PriorityOrder int32 `db:"priority_order" json:"priorityOrder"` + Note pgtype.Text `db:"note" json:"note"` + Metadata []byte `db:"metadata" json:"metadata"` + ID int64 `db:"id" json:"id"` +} + +func (q *Queries) UpdateInvoiceConfigItem(ctx context.Context, arg UpdateInvoiceConfigItemParams) (InvoiceConfigItem, error) { + row := q.db.QueryRow(ctx, updateInvoiceConfigItem, + arg.RequiredQuantity, + arg.AllowAlternative, + arg.PriorityOrder, + arg.Note, + arg.Metadata, + arg.ID, + ) + var i InvoiceConfigItem + err := row.Scan( + &i.ID, + &i.InvoiceConfigID, + &i.ComponentID, + &i.RequiredQuantity, + &i.AllowAlternative, + &i.PriorityOrder, + &i.Note, + &i.Metadata, + ) + return i, err +} diff --git a/sqlc_gen/querier.go b/sqlc_gen/querier.go index f9a6346..bea25a5 100644 --- a/sqlc_gen/querier.go +++ b/sqlc_gen/querier.go @@ -21,6 +21,7 @@ type Querier interface { CreateComponentType(ctx context.Context, arg CreateComponentTypeParams) (ComponentType, error) CreateContainer(ctx context.Context, arg CreateContainerParams) (Container, error) CreateInvoiceConfig(ctx context.Context, arg CreateInvoiceConfigParams) (InvoiceConfig, error) + CreateInvoiceConfigItem(ctx context.Context, arg CreateInvoiceConfigItemParams) (InvoiceConfigItem, 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) @@ -33,6 +34,7 @@ type Querier interface { DeleteComponentType(ctx context.Context, id int64) (int64, error) DeleteContainer(ctx context.Context, id int64) (int64, error) DeleteInvoiceConfig(ctx context.Context, id int64) (int64, error) + DeleteInvoiceConfigItem(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) @@ -46,6 +48,7 @@ type Querier interface { GetComponentTypeByID(ctx context.Context, id int64) (ComponentType, error) GetContainerByID(ctx context.Context, id int64) (Container, error) GetInvoiceConfigByID(ctx context.Context, id int64) (InvoiceConfig, error) + GetInvoiceConfigItemByID(ctx context.Context, id int64) (InvoiceConfigItem, 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) @@ -62,6 +65,7 @@ type Querier interface { ListComponentTypes(ctx context.Context) ([]ComponentType, error) ListComponents(ctx context.Context) ([]Component, error) ListContainers(ctx context.Context) ([]Container, error) + ListInvoiceConfigItems(ctx context.Context) ([]InvoiceConfigItem, error) ListInvoiceConfigs(ctx context.Context) ([]InvoiceConfig, error) ListRoles(ctx context.Context) ([]Role, error) ListRooms(ctx context.Context) ([]Room, error) @@ -78,6 +82,7 @@ type Querier interface { UpdateComponentType(ctx context.Context, arg UpdateComponentTypeParams) (ComponentType, error) UpdateContainer(ctx context.Context, arg UpdateContainerParams) (Container, error) UpdateInvoiceConfig(ctx context.Context, arg UpdateInvoiceConfigParams) (InvoiceConfig, error) + UpdateInvoiceConfigItem(ctx context.Context, arg UpdateInvoiceConfigItemParams) (InvoiceConfigItem, 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)