Files
vapor-po/Sources/App/Controllers/PurchaseOrderViewController.swift

141 lines
3.8 KiB
Swift

import Fluent
import Vapor
struct PurchaseOrderViewController: RouteCollection {
private let api = ApiController()
func boot(routes: any RoutesBuilder) throws {
let pos = routes.protected.grouped("purchase-orders")
pos.get(use: index(req:))
pos.post(use: create(req:))
}
@Sendable
func index(req: Request) async throws -> View {
let purchaseOrders = try await api.purchaseOrdersIndex(req: req)
let branches = try await api.getBranches(req: req)
let employees = try await api.employeesIndex(req: req)
req.logger.info("Branches: \(branches)")
return try await req.view.render(
"purchaseOrders/index",
PurchaseOrderCTX(
purchaseOrders: purchaseOrders,
form: .create(branches: branches, employees: employees)
)
)
}
@Sendable
func create(req: Request) async throws -> View {
try PurchaseOrder.FormCreate.validate(content: req)
let createdById = try req.auth.require(User.self).requireID()
let create = try req.content.decode(PurchaseOrder.FormCreate.self)
guard let employee = try await Employee.find(create.createdForID, on: req.db) else {
throw Abort(.notFound, reason: "Employee not found.")
}
guard employee.active else {
throw Abort(.badRequest, reason: "Employee is not active, unable to generate a PO for in-active employees")
}
let purchaseOrder = create.toModel(createdByID: createdById)
try await purchaseOrder.save(on: req.db)
let purchaseOrders = try await api.purchaseOrdersIndex(req: req)
return try await req.view.render("purchaseOrders/table", ["purchaseOrders": purchaseOrders])
}
}
private struct PurchaseOrderCTX: Content {
let purchaseOrders: [PurchaseOrder.DTO]
let form: PurchaseOrderFormCTX?
}
private struct PurchaseOrderFormCTX: Content {
let htmxForm: HtmxFormCTX<Context>
struct Context: Content {
let branches: [VendorBranch.FormDTO]
let employees: [Employee.DTO]
}
static func create(branches: [VendorBranch.FormDTO], employees: [Employee.DTO]) -> Self {
.init(htmxForm: .init(
formClass: "po-form",
formId: "po-form",
htmxTargetUrl: .post("/purchase-orders"),
htmxTarget: "#po-table",
htmxPushUrl: false,
htmxResetAfterRequest: true,
htmxSwapOob: nil,
htmxSwap: .outerHTML,
context: .init(branches: branches, employees: employees)
))
}
}
extension VendorBranch {
struct FormDTO: Content {
let id: UUID
let name: String
let vendor: Vendor.DTO
}
func toFormDTO() throws -> FormDTO {
try .init(
id: requireID(),
name: name,
vendor: vendor.toDTO()
)
}
}
private extension PurchaseOrder {
struct FormCreate: Content {
let id: Int?
let workOrder: String?
let materials: String
let customer: String
let truckStock: Bool?
let createdForID: Employee.IDValue
let vendorBranchID: VendorBranch.IDValue
func toModel(createdByID: User.IDValue) -> PurchaseOrder {
.init(
id: id,
workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil,
materials: materials,
customer: customer,
truckStock: truckStock ?? false,
createdByID: createdByID,
createdForID: createdForID,
vendorBranchID: vendorBranchID,
createdAt: nil,
updatedAt: nil
)
}
}
}
private extension ApiController {
func getBranches(req: Request) async throws -> [VendorBranch.FormDTO] {
try await VendorBranch.query(on: req.db)
.with(\.$vendor)
// .sort(Vendor.self, \.$name)
.all()
.map { try $0.toFormDTO() }
}
}
extension PurchaseOrder.FormCreate: Validatable {
static func validations(_ validations: inout Validations) {
validations.add("materials", as: String.self, is: !.empty)
validations.add("customer", as: String.self, is: !.empty)
}
}