material_manage_system/internal/repository/application.go
2026-09-15 16:08:18 +08:00

341 lines
10 KiB
Go

package repository
import (
"database/sql"
"github.com/wmms/wmms/internal/model"
"github.com/wmms/wmms/pkg/database"
)
type ApplicationRepository struct{}
func NewApplicationRepository() *ApplicationRepository {
return &ApplicationRepository{}
}
func (r *ApplicationRepository) Create(app *model.MaterialApplication) error {
tx, err := database.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback()
query := `INSERT INTO material_applications (user_id, purpose, status) VALUES (?, ?, ?)`
result, err := tx.Exec(query, app.UserID, app.Purpose, app.Status)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
app.ID = id
// Insert application items
for _, item := range app.Items {
itemQuery := `INSERT INTO application_items (application_id, material_id, quantity) VALUES (?, ?, ?)`
itemResult, err := tx.Exec(itemQuery, app.ID, item.MaterialID, item.Quantity)
if err != nil {
return err
}
itemID, _ := itemResult.LastInsertId()
item.ID = itemID
}
return tx.Commit()
}
func (r *ApplicationRepository) GetByID(id int64) (*model.MaterialApplication, error) {
query := `SELECT a.id, a.user_id, a.purpose, a.status, a.rejection_reason, a.approved_by, a.created_at, a.updated_at,
u.username
FROM material_applications a
JOIN users u ON a.user_id = u.id
WHERE a.id = ?`
var app model.MaterialApplication
err := database.DB.QueryRow(query, id).Scan(
&app.ID, &app.UserID, &app.Purpose, &app.Status,
&app.RejectionReason, &app.ApprovedBy, &app.CreatedAt, &app.UpdatedAt,
&app.Username,
)
if err != nil {
return nil, err
}
// Fetch items
app.Items, err = r.GetApplicationItems(id)
if err != nil {
return nil, err
}
return &app, nil
}
func (r *ApplicationRepository) GetApplicationItems(applicationID int64) ([]model.ApplicationItem, error) {
query := `SELECT ai.id, ai.application_id, ai.material_id, ai.quantity, m.material_code, m.name
FROM application_items ai
JOIN materials m ON ai.material_id = m.id
WHERE ai.application_id = ?`
rows, err := database.DB.Query(query, applicationID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []model.ApplicationItem
for rows.Next() {
var item model.ApplicationItem
err := rows.Scan(&item.ID, &item.ApplicationID, &item.MaterialID, &item.Quantity, &item.MaterialCode, &item.MaterialName)
if err != nil {
return nil, err
}
items = append(items, item)
}
return items, nil
}
func (r *ApplicationRepository) ListAll(status string, userID int64, limit, offset int) ([]model.MaterialApplication, int, error) {
// Count query
countQuery := `SELECT COUNT(*) FROM material_applications a WHERE 1=1`
var countArgs []interface{}
if status != "" {
countQuery += " AND a.status = ?"
countArgs = append(countArgs, status)
}
if userID > 0 {
countQuery += " AND a.user_id = ?"
countArgs = append(countArgs, userID)
}
var total int
err := database.DB.QueryRow(countQuery, countArgs...).Scan(&total)
if err != nil {
return nil, 0, err
}
// Data query
query := `SELECT a.id, a.user_id, a.purpose, a.status, a.rejection_reason, a.approved_by, a.created_at, a.updated_at,
u.username
FROM material_applications a
JOIN users u ON a.user_id = u.id
WHERE 1=1`
var args []interface{}
if status != "" {
query += " AND a.status = ?"
args = append(args, status)
}
if userID > 0 {
query += " AND a.user_id = ?"
args = append(args, userID)
}
query += " ORDER BY a.created_at DESC LIMIT ? OFFSET ?"
args = append(args, limit, offset)
rows, err := database.DB.Query(query, args...)
if err != nil {
return nil, 0, err
}
defer rows.Close()
var apps []model.MaterialApplication
for rows.Next() {
var app model.MaterialApplication
err := rows.Scan(
&app.ID, &app.UserID, &app.Purpose, &app.Status,
&app.RejectionReason, &app.ApprovedBy, &app.CreatedAt, &app.UpdatedAt,
&app.Username,
)
if err != nil {
return nil, 0, err
}
// Fetch items for each application
app.Items, err = r.GetApplicationItems(app.ID)
if err != nil {
return nil, 0, err
}
apps = append(apps, app)
}
return apps, total, nil
}
func (r *ApplicationRepository) ListByUserID(userID int64, status string, limit, offset int) ([]model.MaterialApplication, int, error) {
return r.ListAll(status, userID, limit, offset)
}
func (r *ApplicationRepository) ListAllApplications(status string, limit, offset int) ([]model.MaterialApplication, int, error) {
return r.ListAll(status, 0, limit, offset)
}
func (r *ApplicationRepository) UpdateStatus(id int64, status string) error {
query := `UPDATE material_applications SET status = ?, updated_at = NOW() WHERE id = ?`
_, err := database.DB.Exec(query, status, id)
return err
}
func (r *ApplicationRepository) Approve(id int64, approverID int64) error {
query := `UPDATE material_applications SET status = ?, approved_by = ?, updated_at = NOW() WHERE id = ?`
_, err := database.DB.Exec(query, model.ApplicationStatusApproved, approverID, id)
return err
}
func (r *ApplicationRepository) Reject(id int64, reason string) error {
query := `UPDATE material_applications SET status = ?, rejection_reason = ?, updated_at = NOW() WHERE id = ?`
_, err := database.DB.Exec(query, model.ApplicationStatusRejected, reason, id)
return err
}
func (r *ApplicationRepository) Cancel(id int64) error {
return r.UpdateStatus(id, model.ApplicationStatusCancelled)
}
func (r *ApplicationRepository) GetByIDWithDetails(id int64) (*model.MaterialApplication, error) {
return r.GetByID(id)
}
func (r *ApplicationRepository) GetMaterialCurrentStock(materialID int64) (int, error) {
query := `SELECT current_stock FROM materials WHERE id = ?`
var stock int
err := database.DB.QueryRow(query, materialID).Scan(&stock)
return stock, err
}
func (r *ApplicationRepository) GetMaterialCurrentStockTx(tx *sql.Tx, materialID int64) (int, error) {
query := `SELECT current_stock FROM materials WHERE id = ? FOR UPDATE`
var stock int
err := tx.QueryRow(query, materialID).Scan(&stock)
return stock, err
}
func (r *ApplicationRepository) DeductStock(materialID int64, quantity int) error {
query := `UPDATE materials SET current_stock = current_stock - ? WHERE id = ?`
_, err := database.DB.Exec(query, quantity, materialID)
return err
}
func (r *ApplicationRepository) DeductStockTx(tx *sql.Tx, materialID int64, quantity int) error {
query := `UPDATE materials SET current_stock = current_stock - ? WHERE id = ?`
_, err := tx.Exec(query, quantity, materialID)
return err
}
func (r *ApplicationRepository) CreateOutboundOrder(order *model.OutboundOrder) error {
query := `INSERT INTO outbound_orders (outbound_date, handler, receiver, status) VALUES (NOW(), ?, ?, ?)`
result, err := database.DB.Exec(query, order.Handler, order.Receiver, order.Status)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
order.ID = id
return nil
}
func (r *ApplicationRepository) CreateOutboundOrderTx(tx *sql.Tx, order *model.OutboundOrder) error {
query := `INSERT INTO outbound_orders (outbound_date, handler, receiver, status) VALUES (NOW(), ?, ?, ?)`
result, err := tx.Exec(query, order.Handler, order.Receiver, order.Status)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
order.ID = id
return nil
}
func (r *ApplicationRepository) CreateOutboundItem(item *model.OutboundItem) error {
query := `INSERT INTO outbound_items (outbound_order_id, material_id, quantity) VALUES (?, ?, ?)`
result, err := database.DB.Exec(query, item.OutboundOrderID, item.MaterialID, item.Quantity)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
item.ID = id
return nil
}
func (r *ApplicationRepository) CreateOutboundItemTx(tx *sql.Tx, item *model.OutboundItem) error {
query := `INSERT INTO outbound_items (outbound_order_id, material_id, quantity) VALUES (?, ?, ?)`
result, err := tx.Exec(query, item.OutboundOrderID, item.MaterialID, item.Quantity)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
item.ID = id
return nil
}
func (r *ApplicationRepository) CreateStockLog(log *model.StockLog) error {
query := `INSERT INTO stock_logs (material_id, type, quantity, related_order_type, related_order_id) VALUES (?, ?, ?, ?, ?)`
result, err := database.DB.Exec(query, log.MaterialID, log.Type, log.Quantity, log.RelatedOrderType, log.RelatedOrderID)
if err != nil {
return err
}
_, err = result.LastInsertId()
return err
}
func (r *ApplicationRepository) CreateStockLogTx(tx *sql.Tx, log *model.StockLog) error {
query := `INSERT INTO stock_logs (material_id, type, quantity, related_order_type, related_order_id) VALUES (?, ?, ?, ?, ?)`
result, err := tx.Exec(query, log.MaterialID, log.Type, log.Quantity, log.RelatedOrderType, log.RelatedOrderID)
if err != nil {
return err
}
_, err = result.LastInsertId()
return err
}
func (r *ApplicationRepository) ApproveTx(tx *sql.Tx, id int64, approverID int64) error {
query := `UPDATE material_applications SET status = ?, approved_by = ?, updated_at = NOW() WHERE id = ?`
_, err := tx.Exec(query, model.ApplicationStatusApproved, approverID, id)
return err
}
func (r *ApplicationRepository) GetApproverName(userID int64) (string, error) {
query := `SELECT username FROM users WHERE id = ?`
var username string
err := database.DB.QueryRow(query, userID).Scan(&username)
return username, err
}
func (r *ApplicationRepository) InitApplicationTable() error {
query := `
CREATE TABLE IF NOT EXISTS material_applications (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
purpose VARCHAR(500) DEFAULT '',
status ENUM('pending','approved','rejected','cancelled') NOT NULL DEFAULT 'pending',
rejection_reason VARCHAR(500) DEFAULT '',
approved_by BIGINT DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)`
_, err := database.DB.Exec(query)
if err != nil {
return err
}
// Create application_items table
itemsQuery := `
CREATE TABLE IF NOT EXISTS application_items (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
application_id BIGINT NOT NULL,
material_id BIGINT NOT NULL,
quantity INT NOT NULL,
FOREIGN KEY (application_id) REFERENCES material_applications(id) ON DELETE CASCADE,
FOREIGN KEY (material_id) REFERENCES materials(id)
)`
_, err = database.DB.Exec(itemsQuery)
return err
}