Skip to content

OBPIH-6502 Add API endpoints to edit prepaid invoice items#4800

Merged
awalkowiak merged 3 commits intofeature/OBPIH-6398-partial-invoices-for-prepaid-pofrom
OBPIH-6502
Aug 26, 2024
Merged

OBPIH-6502 Add API endpoints to edit prepaid invoice items#4800
awalkowiak merged 3 commits intofeature/OBPIH-6398-partial-invoices-for-prepaid-pofrom
OBPIH-6502

Conversation

@awalkowiak
Copy link
Collaborator

✨ Description of Change

Link to GitHub issue or Jira ticket:
https://pihemr.atlassian.net/browse/OBPIH-6502

Description:
Add API endpoints to edit prepaid invoice items


📈 Test Plan

We require that all code changes come paired with a method of testing them. Please select which of the following testing approaches you've included with this change:

  • Frontend automation tests (unit)
  • Backend automation tests (unit, API, smoke)
  • End-to-end tests (Playwright)
  • Manual tests (please describe below)
  • Not Applicable

Description of test plan (if applicable):
Will be tested with frontend changes


✅ Quality Checks

Please confirm and check each of the following to ensure that your change conforms to the coding standards of the project:

  • The pull request title is prefixed with the issue/ticket number (Ex: [OBS-123] for Jira, [#0000] for GitHub, or [OBS-123, OBPIH-123] if there are multiple), or with [N/A] if not applicable
  • The pull request description has enough information for someone without context to be able to understand the change and why it is needed
  • The change has tests that prove the issue is fixed / the feature works as intended

@github-actions github-actions bot added the domain: backend Changes or discussions relating to the backend server label Aug 22, 2024
Integer quantityAvailableToInverse = inverseItem.quantity + getQuantityAvailableToInverse(orderItem, prepaymentItem)
Integer quantityToInverse = quantity > quantityAvailableToInverse ? quantityAvailableToInverse : quantity
inverseItem.quantity = quantityToInverse
inverseItem.amount = quantityToInverse * prepaymentItem.unitPrice * orderItem.order.prepaymentPercent * (-1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ewaterman I still remember you wanted to move it to separate method but I was not sure how to actually do it. Is something like that what you'd expect here:

BigDecimal calculateAmount(Integer quantity, BigDecimal unitPrice, BigDecimal percentage = 1, Integer inverse = 1) {
    return quantity * unitPrice * percentage * inverse
}

and then just passing what we have available or need to use in calculation, or do you see here something a bit more complex in this method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the thing I'm mainly worried about is that we calculate amount all over the place and it'd be nice to have a calculateAmountForPrepaymentItem(), calculateAmountForRegularItem() and calculateAmountForInverseItem() methods just to have it unified.

I haven't thought it through fully so this might not be the best idea, but they could maybe just take in the invoice item itself and do all the lookups they need so that the caller doesn't need to know what to do.

The logic here is just a bit tough to navigate with all the transients and chained calls so it'd be great if there was a simple method of calculating it that brought it all together

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think I am going to leave it as is for now, but I'll think about this as a standalone improvement in separate ticket/pr

action = [POST: "validateInvoiceItem"]
}

"/api/prepaymentInvoices/$id/invoiceItems" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not generally a fan of the camelcase URIs. I usually try to stick to a feature/sub-feature/entity-based system with something like: /api/invoices/prepayment/$id/items or /api/prepayment-invoices/$id/invoice-items. Up to you though

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a written convention for these yet (camel vs kebab case), hence I am following for now what we have in other cases (ex. stockMovements, partialReceiving, etc)


class PrepaymentInvoiceApiController {

def prepaymentInvoiceService
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my opinion we should use proper types unless it's absolutely necessary to use def

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also always trying to catch all of the unnecessary defs in review 🫶

"/api/prepaymentInvoiceItems/$id/" {
controller = "prepaymentInvoiceItemApi"
action = [DELETE: "delete"]
action = [POST: "update", DELETE: "delete"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm doing an update only on the provided fields I usually use PATCH over POST (I think that's the generally accepted convention) but I'm not really bothered either way.

You do have a trailing slash in the URI which is probably best to remove for consistency's sake but up to you.

* change quantity on the corresponding inverse item.
*
* */
void updateItemsQuantity(String invoiceId, List items) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be best to make this method more generic (ie call it updateItems instead of updateItemsQuantity). Right now we only allow you to update the quantity field but that could change in the future.

Integer quantityAvailableToInvoice = invoiceItem.shipmentItem.quantityToInvoice
if (quantity > quantityAvailableToInvoice + invoiceItem.quantity) {
throw new IllegalArgumentException("Cannot update quantity to higher value than available to invoice")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked a similar question in the other PR, but what about cancelled items? You shouldn't be able to update the invoice quantity right? Is that already handled by the quantityToInvoice field? Would it just be 0 in that case?

Copy link
Collaborator Author

@awalkowiak awalkowiak Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Canceled items don't have shipment items, so these are already ruled out and we don't allow to change the quantity on them


// If there is a case that we did not found inverse item, but it was made available to inverse now after
// editing or removing invoice items on invoices, lets try to create it here
inverseItem = createInverseItemForShipmentItem(invoiceItem?.shipmentItem, invoiceItem)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: I'd put this case in the if block (and change the condition to if (!inverseItem)) since it's much simpler and so the big block above doesn't need to be nested inside an if block.

Comment on lines +23 to +28
def update() {
JSONObject jsonObject = request.JSON
Integer quantity = jsonObject.get('quantity')
prepaymentInvoiceService.updateInvoiceItemQuantity(params.id, quantity)
render status: 200
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, from the frontend perspective, update/post methods should return the newly updated state, because we always have to refetch the new data. It's not a change request, because in this one case, on the frontend it's not necessary to fetch the data again, but in most cases, we should.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but here we have a bit more complex update, where we are passing one invoice item, but in reality we are updating two, so should we return both here or only the one that was sent in requests? Due to this, I decided to keep it simple and return only status, at least for now.

@github-actions github-actions bot added the domain: l10n Changes or discussions relating to localization & Internationalization label Aug 23, 2024
Invoice invoice = Invoice.get(invoiceId)
if (!invoice) {
String defaultMessage = "Cannot find invoice with id: ${invoiceId}"
throw new IllegalArgumentException(g.message(code: "invoice.cannotFind.label", args: [invoiceId], default: defaultMessage))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't typically bother setting a default value for localization strings do we?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't have clear convention

@awalkowiak awalkowiak merged commit 0778a20 into feature/OBPIH-6398-partial-invoices-for-prepaid-po Aug 26, 2024
@awalkowiak awalkowiak deleted the OBPIH-6502 branch August 26, 2024 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain: backend Changes or discussions relating to the backend server domain: l10n Changes or discussions relating to localization & Internationalization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants