202 lines
5.8 KiB
Swift
202 lines
5.8 KiB
Swift
import DatabaseClient
|
|
import FluentKit
|
|
import Foundation
|
|
import SharedModels
|
|
|
|
public extension DatabaseClient.PurchaseOrders {
|
|
|
|
static func live(database: any Database) -> Self {
|
|
.init { create in
|
|
let model = try create.toModel()
|
|
try await model.save(on: database)
|
|
let fetched = try await PurchaseOrderModel.allQuery(on: database)
|
|
.filter(\PurchaseOrderModel.$id == model.id!).first()!
|
|
return try fetched.toDTO()
|
|
} fetchAll: {
|
|
try await PurchaseOrderModel.allQuery(on: database)
|
|
.all()
|
|
.map { try $0.toDTO() }
|
|
} fetchPage: { request in
|
|
try await PurchaseOrderModel.allQuery(on: database)
|
|
.paginate(request)
|
|
.map { try $0.toDTO() }
|
|
} get: { id in
|
|
try await PurchaseOrderModel.allQuery(on: database)
|
|
.filter(\PurchaseOrderModel.$id == id)
|
|
.first()
|
|
.map { try $0.toDTO() }
|
|
} delete: { id in
|
|
guard let model = try await PurchaseOrderModel.find(id, on: database) else {
|
|
throw NotFoundError()
|
|
}
|
|
try await model.delete(on: database)
|
|
} search: { search, page in
|
|
let query = PurchaseOrderModel.allQuery(on: database)
|
|
|
|
switch search {
|
|
case let .employee(id):
|
|
return try await query.filter(\PurchaseOrderModel.$createdFor.$id == id)
|
|
.paginate(page)
|
|
.map { try $0.toDTO() }
|
|
|
|
case let .customer(search):
|
|
return try await query.filter(\PurchaseOrderModel.$customer ~~ search)
|
|
.paginate(page)
|
|
.map { try $0.toDTO() }
|
|
|
|
case let .vendor(id):
|
|
return try await query.filter(\PurchaseOrderModel.$vendorBranch.$id == id)
|
|
.paginate(page)
|
|
.map { try $0.toDTO() }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension Page where T == PurchaseOrder {
|
|
static var empty: Self {
|
|
.init(items: [], metadata: .init(page: 1, per: 1, total: 0))
|
|
}
|
|
}
|
|
|
|
extension PurchaseOrder {
|
|
struct Migrate: AsyncMigration {
|
|
|
|
let name = "CreatePurchaseOrder"
|
|
|
|
func prepare(on database: any Database) async throws {
|
|
try await database.schema(PurchaseOrderModel.schema)
|
|
.field("id", .int, .identifier(auto: true))
|
|
.field("work_order", .int)
|
|
.field("customer", .string, .required)
|
|
.field("materials", .string, .required)
|
|
.field("truck_stock", .bool, .required)
|
|
.field("created_by_id", .uuid, .required, .references(UserModel.schema, "id"))
|
|
.field("created_for_id", .uuid, .required, .references(EmployeeModel.schema, "id"))
|
|
.field("vendor_branch_id", .uuid, .required, .references(VendorBranchModel.schema, "id"))
|
|
.field("created_at", .datetime)
|
|
.field("updated_at", .datetime)
|
|
.create()
|
|
}
|
|
|
|
func revert(on database: any Database) async throws {
|
|
try await database.schema(PurchaseOrderModel.schema).delete()
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extension PurchaseOrder.Create {
|
|
|
|
func toModel() throws -> PurchaseOrderModel {
|
|
try validate()
|
|
return .init(
|
|
materials: materials,
|
|
customer: customer,
|
|
truckStock: truckStock ?? false,
|
|
createdByID: createdByID,
|
|
createdForID: createdForID,
|
|
vendorBranchID: vendorBranchID
|
|
)
|
|
}
|
|
|
|
func validate() throws {
|
|
guard !materials.isEmpty else {
|
|
throw ValidationError(message: "Materials should not be empty.")
|
|
}
|
|
guard !customer.isEmpty else {
|
|
throw ValidationError(message: "Customer should not be empty.")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The purchase order database model.
|
|
///
|
|
/// # NOTE: An initial purchase order should be created with an `id` higher than our current PO
|
|
/// so that subsequent PO's are generated with higher values than our current system produces.
|
|
/// once the first one is set, the rest will auto-increment from there.
|
|
final class PurchaseOrderModel: Model, Codable, @unchecked Sendable {
|
|
static let schema = "purchase_order"
|
|
|
|
@ID(custom: "id", generatedBy: .database)
|
|
var id: Int?
|
|
|
|
@Field(key: "work_order")
|
|
var workOrder: Int?
|
|
|
|
@Field(key: "materials")
|
|
var materials: String
|
|
|
|
@Field(key: "customer")
|
|
var customer: String
|
|
|
|
@Field(key: "truck_stock")
|
|
var truckStock: Bool
|
|
|
|
@Parent(key: "created_by_id")
|
|
var createdBy: UserModel
|
|
|
|
@Parent(key: "created_for_id")
|
|
var createdFor: EmployeeModel
|
|
|
|
@Parent(key: "vendor_branch_id")
|
|
var vendorBranch: VendorBranchModel
|
|
|
|
@Timestamp(key: "created_at", on: .create, format: .iso8601)
|
|
var createdAt: Date?
|
|
|
|
@Timestamp(key: "updated_at", on: .update, format: .iso8601)
|
|
var updatedAt: Date?
|
|
|
|
init() {}
|
|
|
|
init(
|
|
id: Int? = nil,
|
|
workOrder: Int? = nil,
|
|
materials: String,
|
|
customer: String,
|
|
truckStock: Bool,
|
|
createdByID: UserModel.IDValue,
|
|
createdForID: EmployeeModel.IDValue,
|
|
vendorBranchID: VendorBranchModel.IDValue,
|
|
createdAt: Date? = nil,
|
|
updatedAt: Date? = nil
|
|
) {
|
|
self.id = id
|
|
self.workOrder = workOrder
|
|
self.materials = materials
|
|
self.customer = customer
|
|
self.truckStock = truckStock
|
|
$createdBy.id = createdByID
|
|
$createdFor.id = createdForID
|
|
$vendorBranch.id = vendorBranchID
|
|
self.createdAt = createdAt
|
|
self.updatedAt = updatedAt
|
|
}
|
|
|
|
func toDTO() throws -> PurchaseOrder {
|
|
try .init(
|
|
id: requireID(),
|
|
workOrder: workOrder,
|
|
materials: materials,
|
|
customer: customer,
|
|
truckStock: truckStock,
|
|
createdBy: createdBy.toDTO(),
|
|
createdFor: createdFor.toDTO(),
|
|
vendorBranch: vendorBranch.toDetail(),
|
|
createdAt: createdAt,
|
|
updatedAt: updatedAt
|
|
)
|
|
}
|
|
|
|
static func allQuery(on db: any Database) -> QueryBuilder<PurchaseOrderModel> {
|
|
PurchaseOrderModel.query(on: db)
|
|
.sort(\PurchaseOrderModel.$id, .descending)
|
|
.with(\PurchaseOrderModel.$createdBy)
|
|
.with(\PurchaseOrderModel.$createdFor)
|
|
.with(\PurchaseOrderModel.$vendorBranch) { branch in
|
|
branch.with(\VendorBranchModel.$vendor)
|
|
}
|
|
}
|
|
}
|