Skip to content

OBPIH-6889 Implement get cycle counts API#4997

Merged
kchelstowski merged 2 commits intodevelopfrom
OBPIH-6889-2
Jan 20, 2025
Merged

OBPIH-6889 Implement get cycle counts API#4997
kchelstowski merged 2 commits intodevelopfrom
OBPIH-6889-2

Conversation

@kchelstowski
Copy link
Collaborator

✨ Description of Change

A concise summary of what is being changed. Please provide enough context for reviewers to be able to understand the change and why it is necessary. If the issue/ticket already provides enough information, you can put "See ticket" as the description.

Link to GitHub issue or Jira ticket:

Description:


📷 Screenshots & Recordings (optional)

If this PR contains a UI change, consider adding one or more screenshots here or link to a screen recording to help reviewers visualize the change. Otherwise, you can remove this section.

@kchelstowski kchelstowski self-assigned this Jan 15, 2025
@github-actions github-actions bot added domain: frontend Changes or discussions relating to the frontend UI domain: backend Changes or discussions relating to the backend server flag: schema change Hilights a pull request that contains a change to the database schema labels Jan 15, 2025
@codecov
Copy link

codecov bot commented Jan 15, 2025

Codecov Report

Attention: Patch coverage is 10.00000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 7.69%. Comparing base (3005f87) to head (02c93a7).
Report is 186 commits behind head on develop.

Files with missing lines Patch % Lines
...g/pih/warehouse/inventory/CycleCountService.groovy 0.00% 5 Missing ⚠️
...g/pih/warehouse/api/CycleCountApiController.groovy 0.00% 3 Missing ⚠️
.../inventory/CycleCountCandidateFilterCommand.groovy 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             develop   #4997      +/-   ##
============================================
+ Coverage       7.65%   7.69%   +0.03%     
- Complexity       832     846      +14     
============================================
  Files            609     610       +1     
  Lines          42543   42586      +43     
  Branches       10330   10344      +14     
============================================
+ Hits            3257    3275      +18     
- Misses         38809   38826      +17     
- Partials         477     485       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

}

"/api/facilities/$facilityId/cycle-count-candidates" {
"/api/facilities/$facilityId/cycle-counts" {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

as explained quickly on the tech huddle - I didn't know whether to keep the cycle-count-candidates and create cycle-counts even though they would point to the same endpoint, or to have a common one.
I'm up for suggestions.

Copy link
Member

Choose a reason for hiding this comment

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

yeah it is a bit weird. My other comment suggested using just the one GET /cycle-counts/candidates API for all the tabs.

I get that it's a bit strange since for the other tabs like TO COUNT we could look at it like the primary resource we're fetching is cycle count (and we're joining candidate information to that) but since all tabs need essentially the same information, I think it's fine that they all use the GET candidates API


def getCandidates(CycleCountCandidateFilterCommand filterParams) {
List<CycleCountCandidate> candidates = cycleCountService.getCandidates(filterParams, params.facilityId)
def getCycleCounts(CycleCountCandidateFilterCommand filterParams) {
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 also don't feel comfortable with both getCandidates and getCycleCounts - we base those on CycleCountCandidate, but we might use the endpoint for different usages - either for get candidates (cycleCountRequest = null) or to get cycle counts (to count tab, to resolve tab, to approve tab).

Copy link
Member

Choose a reason for hiding this comment

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

yeah this is where the most amount of weirdness happens. However since the response data is structured the same for both pages, I think it's fine to treat this as the original getCandidates (or maybe we can think of a new name that makes the most sense). We should remember that the backend has no knowledge of what the frontend is doing, so the API (and method names) should ideally be reflecting only what the data has (and not what it is being used for). Some other frontend could use the API in a different way.

I'll try to think of a method/API name that makes sense.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point not to think about it in the frontend way. I always end up making that mistake.
I changed the url to /cycle-counts/candidates as suggested and kept the methods with the candidates sufix.

LEFT OUTER JOIN cycle_count_request ON product.id = cycle_count_request.product_id AND location.id = cycle_count_request.facility_id
WHERE product_availability.quantity_on_hand > 0
GROUP BY location.id, product.id, abc_class
AND (cycle_count_request.id IS NULL OR (cycle_count_request.status <> 'COMPLETED' AND cycle_count_request.status <> 'CANCELED'))
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

basically we have to join the cycle count request table, but since we won't delete the completed and canceled requests, we need to filter them out in order to have unique result per product/facility pair.
We have a constraint for product/facility pair to be unique, but only if a request is not completed or canceled.
Since the candidates don't need to look into completed and canceled requests, I filtered them out.

Copy link
Member

Choose a reason for hiding this comment

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

Does the "cycle_count_request.id IS NULL" work if there isn't a cycle count request? I'd assume it'd fail if cycle_count_request is null.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

hmm, why would it fail in your opinion?
We LEFT JOIN the cycle_count_request table, so essentially we take all the rows from the cycle_count_session table, and join the cycle_count_request even if it doesn't exist (so then all the rows from cycle_count_request would be null).
So this check basically does this: return all the records from the cycle_count_session where the cycle count request doesn't exist or its status is not neither COMPLETED nor CANCELED.

Copy link
Member

Choose a reason for hiding this comment

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

I was thinking too Java-based. You're right about the left join

eq("cycleCountRequest.status", command.status)
return
}
isNull("cycleCountRequest")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

e.g.

  1. for "to count" tab we will filter by CREATED status
  2. for all products tab we don't provide status, so we want to search for candidates that do not have the cycle count request created

Copy link
Member

Choose a reason for hiding this comment

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

We also need to filter out requests that are currently in progress. Those shouldn't show up on the all products tab.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

on all products tab we don't provide the status property, hence we would return all candidates that DO NOT have the cycle count request.

Copy link
Member

Choose a reason for hiding this comment

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

but the all products tab shouldn't show any candidates that have an in progress count. You shouldn't be able to start the count if one is already in progress

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

and they won't show, because the cycle count request would already exist for such candidate, so those would be filtered out.

Copy link
Member

Choose a reason for hiding this comment

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

is that true? Maybe I'm misunderstanding, but for all products tab we don't provide a status and so we do isNull("cycleCountRequest") and fetch only the rows that don't have a request.

I don't want to go in circles about this so if you tested it and it's working, I trust you and we can move on 😆

Copy link
Member

Choose a reason for hiding this comment

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

We discussed on slack. I'm being silly. This code works.

}

"/api/facilities/$facilityId/cycle-count-candidates" {
"/api/facilities/$facilityId/cycle-counts" {
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 this API should still be /cycle-count-candidates (though I'd make it /cycle-count/candidates) since ultimately it is candidates focused. (It's returning List so fundamentally it's about candidates.)

I also wanted to use the GET /cycle-counts API for another purpose: https://pihemr.atlassian.net/wiki/spaces/OB/pages/3332505612/Cycle+Count+Items+API#Get-Cycle-Counts

GET /api/facilities/{facility_id}/cycle-counts?ids=1,2,3

[
  {
    request: { ... },
    cycleCount: {
      id: "1",
      ...other fields...
      cycleCountItems: [ ... ],
    },
  },
  ...
]

So it's an API for simply fetching cycle counts, their items, and associated requests, which we (might) need when loading the count page for an already in progress count.

I like being able to have the CRUD style endpoints for cycle count using "cycle-counts" as a base resource. That way the /candidates API can stay as its own thing.

  • GET /cycle-counts
  • GET /cycle-counts/{cycle_count_id}
  • GET /cycle-counts/candidates

Copy link
Member

Choose a reason for hiding this comment

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

(sorry I wrote this before I saw your messages. I didn't refresh the page.)

@kchelstowski
Copy link
Collaborator Author

@awalkowiak to unblock the rest of the PRs and the testing, I'm merging this one as it is. If you have any suggestions/change requests, let me know and I will address them separately.

@kchelstowski kchelstowski merged commit e011448 into develop Jan 20, 2025
9 checks passed
@kchelstowski kchelstowski deleted the OBPIH-6889-2 branch January 20, 2025 10:16
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: frontend Changes or discussions relating to the frontend UI flag: schema change Hilights a pull request that contains a change to the database schema

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants