class InventoryPurchaseService { boolean transactional = false def authService def dateUtilService def inventoryMovementService /** * Calulates the quantities for an inventoryItem and purchaseOrderNumber. * @param order An inventory puchase that was the source of the order. * @returns A result map contianing the totalOrdered, totalReceived, totalRemaining, thisOrderRemaining. */ def calcQuantities(order) { def result = [:] result.totalOrdered = 0 InventoryItemPurchase.withCriteria { eq("inventoryItem", order.inventoryItem) eq("purchaseOrderNumber", order.purchaseOrderNumber) inventoryItemPurchaseType { eq("id", 1L) } }.each() { result.totalOrdered += it.quantity } result.totalReceived = 0 InventoryItemPurchase.withCriteria { eq("inventoryItem", order.inventoryItem) eq("purchaseOrderNumber", order.purchaseOrderNumber) inventoryItemPurchaseType { or { eq("id", 2L) eq("id", 3L) } } }.each() { result.totalReceived += it.quantity } result.totalRemaining if(result.totalOrdered > result.totalReceived) result.totalRemaining = result.totalOrdered - result.totalReceived else result.totalRemaining = 0 result.thisOrderRemaining if(result.totalRemaining > order.quantity) result.thisOrderRemaining = order.quantity else result.thisOrderRemaining = result.totalRemaining return result } def delete(params) { InventoryItemPurchase.withTransaction { status -> def result = [:] def fail = { Map m -> status.setRollbackOnly() if(result.inventoryItemPurchase && m.field) result.inventoryItemPurchase.errors.rejectValue(m.field, m.code) result.error = [ code: m.code, args: ["InventoryItemPurchase", params.id] ] return result } result.inventoryItemPurchaseInstance = InventoryItemPurchase.get(params.id) if(!result.inventoryItemPurchaseInstance) return fail(code:"default.not.found") result.inventoryItemId = result.inventoryItemPurchaseInstance.inventoryItem.id def purchaseTypeId = result.inventoryItemPurchaseInstance.inventoryItemPurchaseType.id // Handle Invoice Payment Approved. // Find and mark all orders as invoicePaymentApproved = false. if(purchaseTypeId == 4) { InventoryItemPurchase.withCriteria { eq("inventoryItem", result.inventoryItemPurchaseInstance.inventoryItem) eq("purchaseOrderNumber", result.inventoryItemPurchaseInstance.purchaseOrderNumber) inventoryItemPurchaseType { eq("id", 1L) // Order Placed. } }.each() { it.invoicePaymentApproved = false } } // Handle Received. // Refuse to delete if payment approved. // Find and reverse the matching movement. // Find and mark all orders as receivedComplete = false. if(purchaseTypeId == 2 || purchaseTypeId == 3) { def paymentAlreadyApproved = InventoryItemPurchase.withCriteria { eq("inventoryItem", result.inventoryItemPurchaseInstance.inventoryItem) eq("purchaseOrderNumber", result.inventoryItemPurchaseInstance.purchaseOrderNumber) inventoryItemPurchaseType { eq("id", 4L) // Invoice Payment Approved. } } if(paymentAlreadyApproved) return fail(code:"inventoryItemPurchase.delete.failure.payment.approved") def startOfDay = dateUtilService.getMidnight(result.inventoryItemPurchaseInstance.dateEntered) def inventoryMovements = InventoryMovement.withCriteria { eq("inventoryItem", result.inventoryItemPurchaseInstance.inventoryItem ) eq("quantity", result.inventoryItemPurchaseInstance.quantity) between("date", startOfDay, startOfDay+1) inventoryMovementType { eq("id", 3L) //purchaseReceived. } order('id', 'desc') // The newest one will be first. } def movementResult = inventoryMovementService.reverseMove(inventoryMovements[0]) if(movementResult.error) return fail(code:"inventoryMovement.quantity.insufficientItemsInStock") InventoryItemPurchase.withCriteria { eq("inventoryItem", result.inventoryItemPurchaseInstance.inventoryItem) eq("purchaseOrderNumber", result.inventoryItemPurchaseInstance.purchaseOrderNumber) inventoryItemPurchaseType { eq("id", 1L) // Order Placed } }.each() { it.receivedComplete = false } } // Handle Received. // Handle Order Placed. // Refuse to delete if we have received items. // Deletion of received already requires payment approved to be deleted. if(purchaseTypeId == 1) { def calcQuantities = calcQuantities(result.inventoryItemPurchaseInstance) if(calcQuantities.totalReceived > 0) return fail(code:"inventoryItemPurchase.delete.failure.received.exists") } // Success. // By this stage everything should be handled and the delete call is allowed and expected to pass. result.inventoryItemPurchaseInstance.delete() return result } // end withTransaction } def edit(params) { def result = [:] def fail = { Map m -> result.error = [ code: m.code, args: ["InventoryItemPurchase", params.id] ] return result } result.inventoryItemPurchaseInstance = InventoryItemPurchase.get(params.id) if(!result.inventoryItemPurchaseInstance) return fail(code:"default.not.found") // Success. return result } def update(params) { InventoryItemPurchase.withTransaction { status -> def result = [:] def fail = { Map m -> status.setRollbackOnly() if(result.inventoryItemPurchaseInstance && m.field) result.inventoryItemPurchaseInstance.errors.rejectValue(m.field, m.code) result.error = [ code: m.code, args: ["InventoryItemPurchase", params.id] ] return result } result.inventoryItemPurchaseInstance = InventoryItemPurchase.get(params.id) if(!result.inventoryItemPurchaseInstance) return fail(code:"default.not.found") // Optimistic locking check. if(params.version) { if(result.inventoryItemPurchaseInstance.version > params.version.toLong()) return fail(field:"version", code:"default.optimistic.locking.failure") } result.inventoryItemPurchaseInstance.properties = params if(result.inventoryItemPurchaseInstance.hasErrors() || !result.inventoryItemPurchaseInstance.save()) return fail(code:"default.update.failure") // Success. return result } //end withTransaction } // end update() def save(params) { InventoryItemPurchase.withTransaction { status -> def result = [:] def fail = { Map m -> status.setRollbackOnly() if(result.inventoryItemPurchase && m.field) result.inventoryItemPurchase.errors.rejectValue(m.field, m.code) result.error = [ code: m.code, args: ["InventoryItemPurchase", params.id] ] return result } result.inventoryItemPurchaseInstance = new InventoryItemPurchase(params) result.inventoryItemPurchaseInstance.enteredBy = authService.currentUser result.inventoryItemPurchaseInstance.inventoryItemPurchaseType = InventoryItemPurchaseType.read(1) // Order // Fetch to prevent lazy initialization error. result.inventoryItemPurchaseInstance.inventoryItem.unitOfMeasure if(result.inventoryItemPurchaseInstance.hasErrors() || !result.inventoryItemPurchaseInstance.save()) return fail(code:"default.create.failure") result.inventoryItemId = result.inventoryItemPurchaseInstance.inventoryItem.id // success return result } // end withTransaction } // save() def receiveSave(params) { InventoryItemPurchase.withTransaction { status -> def result = [:] def fail = { Map m -> status.setRollbackOnly() if(result.inventoryItemPurchase && m.field) result.inventoryItemPurchase.errors.rejectValue(m.field, m.code) result.error = [ code: m.code, args: ["InventoryItemPurchase", params.id] ] return result } def order = InventoryItemPurchase.get(params.orderId) if(!order) return fail(code:"default.not.found") result.orderId = order.id result.inventoryItemPurchaseInstance = new InventoryItemPurchase(params) result.inventoryItemPurchaseInstance.enteredBy = authService.currentUser result.inventoryItemPurchaseInstance.purchaseOrderNumber = order.purchaseOrderNumber result.inventoryItemPurchaseInstance.costCode = order.costCode result.inventoryItemPurchaseInstance.orderValueCurrency = order.orderValueCurrency def calcQuantities = calcQuantities(order) if(result.inventoryItemPurchaseInstance.quantity) calcQuantities.totalReceived += result.inventoryItemPurchaseInstance.quantity if(calcQuantities.totalReceived >= calcQuantities.totalOrdered) { order.receivedComplete = true result.inventoryItemPurchaseInstance.receivedComplete = true result.inventoryItemPurchaseInstance.inventoryItemPurchaseType = InventoryItemPurchaseType.read(3) // Received Complete. } else { order.receivedComplete = false result.inventoryItemPurchaseInstance.inventoryItemPurchaseType = InventoryItemPurchaseType.read(2) // Received B/oder to Come. } // Fetch to prevent lazy initialization error. result.inventoryItemPurchaseInstance.inventoryItem.unitOfMeasure if(order.hasErrors() || !order.save()) return fail(code:"default.create.failure") if(result.inventoryItemPurchaseInstance.hasErrors() || !result.inventoryItemPurchaseInstance.save()) return fail(code:"default.create.failure") result.inventoryItemId = result.inventoryItemPurchaseInstance.inventoryItem.id // Perform the inventory movement. if(result.inventoryItemPurchaseInstance.quantity > 0) { def p = [:] p.inventoryItem = result.inventoryItemPurchaseInstance.inventoryItem p.quantity = result.inventoryItemPurchaseInstance.quantity p.inventoryMovementType = InventoryMovementType.read(3) def movementResult = inventoryMovementService.move(p) if(movementResult.error) return fail(code:"default.create.failure") } // success return result } // end withTransaction } // save() def approveInvoicePaymentSave(params) { InventoryItemPurchase.withTransaction { status -> def result = [:] def fail = { Map m -> status.setRollbackOnly() if(result.inventoryItemPurchaseInstance && m.field) result.inventoryItemPurchaseInstance.errors.rejectValue(m.field, m.code) result.error = [ code: m.code, args: ["InventoryItemPurchase", params.id] ] return result } def order = InventoryItemPurchase.get(params.orderId) if(!order) return fail(code:"default.not.found") result.orderId = order.id result.inventoryItemPurchaseInstance = new InventoryItemPurchase(params) result.inventoryItemPurchaseInstance = new InventoryItemPurchase(params) result.inventoryItemPurchaseInstance.enteredBy = authService.currentUser result.inventoryItemPurchaseInstance.purchaseOrderNumber = order.purchaseOrderNumber result.inventoryItemPurchaseInstance.costCode = order.costCode result.inventoryItemPurchaseInstance.orderValueCurrency = order.orderValueCurrency result.inventoryItemPurchaseInstance.inventoryItemPurchaseType = InventoryItemPurchaseType.read(4) // Approve. order.invoicePaymentApproved = true result.inventoryItemPurchaseInstance.invoicePaymentApproved = true // Fetch to prevent lazy initialization error. result.inventoryItemPurchaseInstance.inventoryItem.unitOfMeasure if(!result.inventoryItemPurchaseInstance.invoiceNumber) return fail(field:"invoiceNumber", code:"inventoryItemPurchase.invoiceNumber.required") order.invoicePaymentApproved = true if(order.hasErrors() || !order.save()) return fail(code:"default.create.failure") if(result.inventoryItemPurchaseInstance.hasErrors() || !result.inventoryItemPurchaseInstance.save()) return fail(code:"default.create.failure") result.inventoryItemId = result.inventoryItemPurchaseInstance.inventoryItem.id // Success.. return result } // end withTransaction } // approveInvoicePaymentSave() } // end class