feat: add endpoints for retrieving stock alerts and anomaly items, including database queries and models
This commit is contained in:
@@ -42,3 +42,24 @@ JOIN cabinets cab ON s.cabinet_id = cab.id
|
||||
JOIN rooms r ON cab.room_id = r.id
|
||||
LEFT JOIN component_items ci ON c.id = ci.container_id
|
||||
WHERE sqlc.narg('warehouse_id')::bigint IS NULL OR r.warehouse_id = sqlc.narg('warehouse_id')::bigint;
|
||||
|
||||
-- name: GetStockAlerts :many
|
||||
SELECT c.id, c.name, c.unit, c.total_quantity, c.min_quantity, c.component_type_id,
|
||||
ct.name AS component_type_name
|
||||
FROM components c
|
||||
LEFT JOIN component_types ct ON c.component_type_id = ct.id
|
||||
WHERE c.total_quantity <= c.min_quantity
|
||||
ORDER BY (c.total_quantity - c.min_quantity) ASC;
|
||||
|
||||
-- name: GetAnomalyItems :many
|
||||
SELECT ci.id, ci.component_id, ci.container_id, ci.quantity, ci.status, ci.created_at, ci.updated_at,
|
||||
c.name AS component_name, c.unit AS component_unit
|
||||
FROM component_items ci
|
||||
JOIN components c ON ci.component_id = c.id
|
||||
JOIN containers con ON ci.container_id = con.id
|
||||
JOIN shelves s ON con.shelf_id = s.id
|
||||
JOIN cabinets cab ON s.cabinet_id = cab.id
|
||||
JOIN rooms r ON cab.room_id = r.id
|
||||
WHERE ci.status != 'normal'
|
||||
AND (sqlc.narg('warehouse_id')::bigint IS NULL OR r.warehouse_id = sqlc.narg('warehouse_id')::bigint)
|
||||
ORDER BY ci.updated_at DESC;
|
||||
|
||||
@@ -2189,6 +2189,108 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/dashboard/anomalies": {
|
||||
"get": {
|
||||
"description": "Retrieve list of component items with abnormal status (damaged, expired, long_unused, pending_inspection)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"dashboard"
|
||||
],
|
||||
"summary": "Get anomaly items",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by warehouse ID",
|
||||
"name": "warehouse_id",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.AnomalyItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/dashboard/stock-alerts": {
|
||||
"get": {
|
||||
"description": "Retrieve list of components that are low on stock (total_quantity \u003c= min_quantity)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"dashboard"
|
||||
],
|
||||
"summary": "Get stock alerts",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.StockAlert"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/dashboard/summary": {
|
||||
"get": {
|
||||
"description": "Retrieve dashboard summary with key statistics",
|
||||
@@ -2202,6 +2304,14 @@ const docTemplate = `{
|
||||
"dashboard"
|
||||
],
|
||||
"summary": "Get dashboard summary",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by warehouse ID",
|
||||
"name": "warehouse_id",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -3904,6 +4014,38 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.AnomalyItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"componentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"componentName": {
|
||||
"type": "string"
|
||||
},
|
||||
"componentUnit": {
|
||||
"type": "string"
|
||||
},
|
||||
"containerId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"quantity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.Cabinet": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4322,6 +4464,32 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.StockAlert": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"componentTypeId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"componentTypeName": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"minQuantity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"totalQuantity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.TodayInvoiceCount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -2183,6 +2183,108 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/dashboard/anomalies": {
|
||||
"get": {
|
||||
"description": "Retrieve list of component items with abnormal status (damaged, expired, long_unused, pending_inspection)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"dashboard"
|
||||
],
|
||||
"summary": "Get anomaly items",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by warehouse ID",
|
||||
"name": "warehouse_id",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.AnomalyItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/dashboard/stock-alerts": {
|
||||
"get": {
|
||||
"description": "Retrieve list of components that are low on stock (total_quantity \u003c= min_quantity)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"dashboard"
|
||||
],
|
||||
"summary": "Get stock alerts",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/response.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.StockAlert"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/dashboard/summary": {
|
||||
"get": {
|
||||
"description": "Retrieve dashboard summary with key statistics",
|
||||
@@ -2196,6 +2298,14 @@
|
||||
"dashboard"
|
||||
],
|
||||
"summary": "Get dashboard summary",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by warehouse ID",
|
||||
"name": "warehouse_id",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -3898,6 +4008,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.AnomalyItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"componentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"componentName": {
|
||||
"type": "string"
|
||||
},
|
||||
"componentUnit": {
|
||||
"type": "string"
|
||||
},
|
||||
"containerId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"quantity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.Cabinet": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4316,6 +4458,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.StockAlert": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"componentTypeId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"componentTypeName": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"minQuantity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"totalQuantity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.TodayInvoiceCount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -26,6 +26,27 @@ definitions:
|
||||
priority:
|
||||
type: integer
|
||||
type: object
|
||||
models.AnomalyItem:
|
||||
properties:
|
||||
componentId:
|
||||
type: integer
|
||||
componentName:
|
||||
type: string
|
||||
componentUnit:
|
||||
type: string
|
||||
containerId:
|
||||
type: integer
|
||||
createdAt:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
quantity:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
models.Cabinet:
|
||||
properties:
|
||||
createdAt:
|
||||
@@ -300,6 +321,23 @@ definitions:
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
models.StockAlert:
|
||||
properties:
|
||||
componentTypeId:
|
||||
type: integer
|
||||
componentTypeName:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
minQuantity:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
totalQuantity:
|
||||
type: integer
|
||||
unit:
|
||||
type: string
|
||||
type: object
|
||||
models.TodayInvoiceCount:
|
||||
properties:
|
||||
count:
|
||||
@@ -2416,11 +2454,79 @@ paths:
|
||||
summary: Update container
|
||||
tags:
|
||||
- container
|
||||
/v1/dashboard/anomalies:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Retrieve list of component items with abnormal status (damaged,
|
||||
expired, long_unused, pending_inspection)
|
||||
parameters:
|
||||
- description: Filter by warehouse ID
|
||||
in: query
|
||||
name: warehouse_id
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/models.AnomalyItem'
|
||||
type: array
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: Get anomaly items
|
||||
tags:
|
||||
- dashboard
|
||||
/v1/dashboard/stock-alerts:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Retrieve list of components that are low on stock (total_quantity
|
||||
<= min_quantity)
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/response.SuccessResponse'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/models.StockAlert'
|
||||
type: array
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.ErrorResponse'
|
||||
summary: Get stock alerts
|
||||
tags:
|
||||
- dashboard
|
||||
/v1/dashboard/summary:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Retrieve dashboard summary with key statistics
|
||||
parameters:
|
||||
- description: Filter by warehouse ID
|
||||
in: query
|
||||
name: warehouse_id
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
||||
@@ -32,3 +32,29 @@ func ToDomainContainerStats(r db.GetContainerStatsRow) models.ContainerStats {
|
||||
EmptyContainers: int64(r.EmptyContainers),
|
||||
}
|
||||
}
|
||||
|
||||
func ToDomainStockAlert(r db.GetStockAlertsRow) models.StockAlert {
|
||||
return models.StockAlert{
|
||||
ID: r.ID,
|
||||
Name: r.Name,
|
||||
Unit: r.Unit,
|
||||
TotalQuantity: r.TotalQuantity,
|
||||
MinQuantity: r.MinQuantity,
|
||||
ComponentTypeID: r.ComponentTypeID,
|
||||
ComponentTypeName: r.ComponentTypeName.String,
|
||||
}
|
||||
}
|
||||
|
||||
func ToDomainAnomalyItem(r db.GetAnomalyItemsRow) models.AnomalyItem {
|
||||
return models.AnomalyItem{
|
||||
ID: r.ID,
|
||||
ComponentID: r.ComponentID,
|
||||
ContainerID: r.ContainerID,
|
||||
Quantity: r.Quantity,
|
||||
Status: string(r.Status),
|
||||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
ComponentName: r.ComponentName,
|
||||
ComponentUnit: r.ComponentUnit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type TotalComponentStats struct {
|
||||
TotalTypes int64 `json:"totalTypes"`
|
||||
TotalQuantity int64 `json:"totalQuantity"`
|
||||
@@ -28,3 +30,25 @@ type DashboardSummary struct {
|
||||
TodayInvoices []TodayInvoiceCount `json:"todayInvoices"`
|
||||
EmptyContainers ContainerStats `json:"emptyContainers"`
|
||||
}
|
||||
|
||||
type StockAlert struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Unit string `json:"unit"`
|
||||
TotalQuantity int32 `json:"totalQuantity"`
|
||||
MinQuantity int32 `json:"minQuantity"`
|
||||
ComponentTypeID int64 `json:"componentTypeId"`
|
||||
ComponentTypeName string `json:"componentTypeName"`
|
||||
}
|
||||
|
||||
type AnomalyItem struct {
|
||||
ID int64 `json:"id"`
|
||||
ComponentID int64 `json:"componentId"`
|
||||
ContainerID int64 `json:"containerId"`
|
||||
Quantity int32 `json:"quantity"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ComponentName string `json:"componentName"`
|
||||
ComponentUnit string `json:"componentUnit"`
|
||||
}
|
||||
|
||||
@@ -59,3 +59,27 @@ func GetDashboardSummary(ctx context.Context, queries *db.Queries, warehouseID p
|
||||
EmptyContainers: mapper.ToDomainContainerStats(containerStats),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetStockAlerts(ctx context.Context, queries *db.Queries) ([]models.StockAlert, error) {
|
||||
results, err := queries.GetStockAlerts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]models.StockAlert, 0, len(results))
|
||||
for _, r := range results {
|
||||
items = append(items, mapper.ToDomainStockAlert(r))
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func GetAnomalyItems(ctx context.Context, queries *db.Queries, warehouseID pgtype.Int8) ([]models.AnomalyItem, error) {
|
||||
results, err := queries.GetAnomalyItems(ctx, warehouseID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]models.AnomalyItem, 0, len(results))
|
||||
for _, r := range results {
|
||||
items = append(items, mapper.ToDomainAnomalyItem(r))
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
@@ -156,6 +156,8 @@ func NewRouter() *gin.Engine {
|
||||
dashboard := protected.Group(constants.API_GROUP_DASHBOARD)
|
||||
{
|
||||
dashboard.GET("/summary", utils.AsyncHandler(services.DashboardSummary))
|
||||
dashboard.GET("/stock-alerts", utils.AsyncHandler(services.DashboardStockAlerts))
|
||||
dashboard.GET("/anomalies", utils.AsyncHandler(services.DashboardAnomalies))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,3 +41,53 @@ func DashboardSummary(c *gin.Context) error {
|
||||
response.Ok(c, "Success", summary)
|
||||
return nil
|
||||
}
|
||||
|
||||
// @Summary Get stock alerts
|
||||
// @Description Retrieve list of components that are low on stock (total_quantity <= min_quantity)
|
||||
// @Tags dashboard
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.SuccessResponse{data=[]models.StockAlert}
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /v1/dashboard/stock-alerts [get]
|
||||
func DashboardStockAlerts(c *gin.Context) error {
|
||||
alerts, err := repositories.GetStockAlerts(c.Request.Context(), global.Queries)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Error when Get Stock Alerts")
|
||||
response.InternalServerError(c, http.StatusInternalServerError, "Failed to get stock alerts")
|
||||
return nil
|
||||
}
|
||||
response.Ok(c, "Success", alerts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// @Summary Get anomaly items
|
||||
// @Description Retrieve list of component items with abnormal status (damaged, expired, long_unused, pending_inspection)
|
||||
// @Tags dashboard
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param warehouse_id query int false "Filter by warehouse ID"
|
||||
// @Success 200 {object} response.SuccessResponse{data=[]models.AnomalyItem}
|
||||
// @Failure 400 {object} response.ErrorResponse
|
||||
// @Failure 500 {object} response.ErrorResponse
|
||||
// @Router /v1/dashboard/anomalies [get]
|
||||
func DashboardAnomalies(c *gin.Context) error {
|
||||
var warehouseID pgtype.Int8
|
||||
if raw := c.Query("warehouse_id"); raw != "" {
|
||||
id, err := strconv.ParseInt(raw, 10, 64)
|
||||
if err != nil {
|
||||
response.BadRequestError(c, http.StatusBadRequest, "Invalid warehouse_id")
|
||||
return nil
|
||||
}
|
||||
warehouseID = pgtype.Int8{Int64: id, Valid: true}
|
||||
}
|
||||
|
||||
anomalies, err := repositories.GetAnomalyItems(c.Request.Context(), global.Queries, warehouseID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Error when Get Anomaly Items")
|
||||
response.InternalServerError(c, http.StatusInternalServerError, "Failed to get anomaly items")
|
||||
return nil
|
||||
}
|
||||
response.Ok(c, "Success", anomalies)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
@@ -72,6 +73,62 @@ func (q *Queries) GetAbnormalItemCounts(ctx context.Context, warehouseID pgtype.
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getAnomalyItems = `-- name: GetAnomalyItems :many
|
||||
SELECT ci.id, ci.component_id, ci.container_id, ci.quantity, ci.status, ci.created_at, ci.updated_at,
|
||||
c.name AS component_name, c.unit AS component_unit
|
||||
FROM component_items ci
|
||||
JOIN components c ON ci.component_id = c.id
|
||||
JOIN containers con ON ci.container_id = con.id
|
||||
JOIN shelves s ON con.shelf_id = s.id
|
||||
JOIN cabinets cab ON s.cabinet_id = cab.id
|
||||
JOIN rooms r ON cab.room_id = r.id
|
||||
WHERE ci.status != 'normal'
|
||||
AND ($1::bigint IS NULL OR r.warehouse_id = $1::bigint)
|
||||
ORDER BY ci.updated_at DESC
|
||||
`
|
||||
|
||||
type GetAnomalyItemsRow struct {
|
||||
ID int64 `db:"id" json:"id"`
|
||||
ComponentID int64 `db:"component_id" json:"componentId"`
|
||||
ContainerID int64 `db:"container_id" json:"containerId"`
|
||||
Quantity int32 `db:"quantity" json:"quantity"`
|
||||
Status ComponentItemStatusEnum `db:"status" json:"status"`
|
||||
CreatedAt time.Time `db:"created_at" json:"createdAt"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
|
||||
ComponentName string `db:"component_name" json:"componentName"`
|
||||
ComponentUnit string `db:"component_unit" json:"componentUnit"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAnomalyItems(ctx context.Context, warehouseID pgtype.Int8) ([]GetAnomalyItemsRow, error) {
|
||||
rows, err := q.db.Query(ctx, getAnomalyItems, warehouseID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetAnomalyItemsRow
|
||||
for rows.Next() {
|
||||
var i GetAnomalyItemsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ComponentID,
|
||||
&i.ContainerID,
|
||||
&i.Quantity,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.ComponentName,
|
||||
&i.ComponentUnit,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getContainerStats = `-- name: GetContainerStats :one
|
||||
SELECT
|
||||
COUNT(*) AS total_containers,
|
||||
@@ -96,6 +153,53 @@ func (q *Queries) GetContainerStats(ctx context.Context, warehouseID pgtype.Int8
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getStockAlerts = `-- name: GetStockAlerts :many
|
||||
SELECT c.id, c.name, c.unit, c.total_quantity, c.min_quantity, c.component_type_id,
|
||||
ct.name AS component_type_name
|
||||
FROM components c
|
||||
LEFT JOIN component_types ct ON c.component_type_id = ct.id
|
||||
WHERE c.total_quantity <= c.min_quantity
|
||||
ORDER BY (c.total_quantity - c.min_quantity) ASC
|
||||
`
|
||||
|
||||
type GetStockAlertsRow struct {
|
||||
ID int64 `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Unit string `db:"unit" json:"unit"`
|
||||
TotalQuantity int32 `db:"total_quantity" json:"totalQuantity"`
|
||||
MinQuantity int32 `db:"min_quantity" json:"minQuantity"`
|
||||
ComponentTypeID int64 `db:"component_type_id" json:"componentTypeId"`
|
||||
ComponentTypeName pgtype.Text `db:"component_type_name" json:"componentTypeName"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetStockAlerts(ctx context.Context) ([]GetStockAlertsRow, error) {
|
||||
rows, err := q.db.Query(ctx, getStockAlerts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetStockAlertsRow
|
||||
for rows.Next() {
|
||||
var i GetStockAlertsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Unit,
|
||||
&i.TotalQuantity,
|
||||
&i.MinQuantity,
|
||||
&i.ComponentTypeID,
|
||||
&i.ComponentTypeName,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getTodayInvoiceCounts = `-- name: GetTodayInvoiceCounts :many
|
||||
SELECT type, COUNT(*) AS count
|
||||
FROM invoices
|
||||
|
||||
@@ -51,6 +51,7 @@ type Querier interface {
|
||||
FindComponentItem(ctx context.Context, componentid int64) ([]FindComponentItemRow, error)
|
||||
GetAbnormalItemCounts(ctx context.Context, warehouseID pgtype.Int8) ([]GetAbnormalItemCountsRow, error)
|
||||
GetAlternativeComponentByID(ctx context.Context, id int64) (AlternativeComponent, error)
|
||||
GetAnomalyItems(ctx context.Context, warehouseID pgtype.Int8) ([]GetAnomalyItemsRow, error)
|
||||
GetCabinetByID(ctx context.Context, id int64) (Cabinet, error)
|
||||
GetComponentByID(ctx context.Context, id int64) (Component, error)
|
||||
GetComponentCodeByID(ctx context.Context, id int64) (ComponentCode, error)
|
||||
@@ -67,6 +68,7 @@ type Querier interface {
|
||||
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)
|
||||
GetStockAlerts(ctx context.Context) ([]GetStockAlertsRow, error)
|
||||
GetTodayInvoiceCounts(ctx context.Context) ([]GetTodayInvoiceCountsRow, error)
|
||||
GetTotalComponentStats(ctx context.Context, warehouseID pgtype.Int8) (GetTotalComponentStatsRow, error)
|
||||
GetUserByEmail(ctx context.Context, email string) (User, error)
|
||||
|
||||
Reference in New Issue
Block a user