From f623c170711f8f73b36c534b6838a78caf5f5c28 Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Tue, 14 Apr 2026 04:03:15 -0600 Subject: [PATCH] added filter by customer for sales to backend and android --- .../petstoremobile/adapters/SaleAdapter.java | 6 +++++ .../example/petstoremobile/api/SaleApi.java | 1 + .../fragments/listfragments/SaleFragment.java | 26 ++++++++++++++++--- .../repositories/SaleRepository.java | 4 +-- .../viewmodels/AnalyticsViewModel.java | 2 +- .../viewmodels/RefundViewModel.java | 2 +- .../viewmodels/SaleListViewModel.java | 22 +++++++++++++--- .../app/src/main/res/layout/fragment_sale.xml | 9 +++++++ android/app/src/main/res/layout/item_sale.xml | 9 +++++++ .../backend/controller/SaleController.java | 3 ++- .../backend/repository/SaleRepository.java | 5 ++-- .../petshop/backend/service/SaleService.java | 4 +-- 12 files changed, 76 insertions(+), 17 deletions(-) diff --git a/android/app/src/main/java/com/example/petstoremobile/adapters/SaleAdapter.java b/android/app/src/main/java/com/example/petstoremobile/adapters/SaleAdapter.java index deca5624..a7b6c2bb 100644 --- a/android/app/src/main/java/com/example/petstoremobile/adapters/SaleAdapter.java +++ b/android/app/src/main/java/com/example/petstoremobile/adapters/SaleAdapter.java @@ -48,6 +48,12 @@ public class SaleAdapter extends RecyclerView.Adapter { + SpinnerUtils.populateWhiteSpinner(requireContext(), binding.spinnerCustomer, list, + CustomerDTO::getFullName, "All Customers", null, CustomerDTO::getCustomerId); + }); + viewModel.getIsLoading().observe(getViewLifecycleOwner(), loading -> { binding.swipeRefreshSale.setRefreshing(loading); }); @@ -98,16 +105,17 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis public void onResume() { super.onResume(); if (!isStaff()) viewModel.loadStores(); + viewModel.loadCustomers(); } private void setupFilterToggle() { if (isStaff()) { UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSale, - binding.spinnerPaymentMethod, binding.spinnerRefundStatus); + binding.spinnerPaymentMethod, binding.spinnerRefundStatus, binding.spinnerCustomer); binding.spinnerStore.setVisibility(View.GONE); } else { UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSale, - binding.spinnerPaymentMethod, binding.spinnerStore, binding.spinnerRefundStatus); + binding.spinnerPaymentMethod, binding.spinnerStore, binding.spinnerRefundStatus, binding.spinnerCustomer); } } @@ -133,6 +141,10 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerRefundStatus, refundStatuses, () -> loadSales(true)); } + private void setupCustomerFilter() { + SpinnerUtils.setupFilterSpinner(binding.spinnerCustomer, () -> loadSales(true)); + } + private void setupRecyclerView() { adapter = new SaleAdapter(saleList, this); binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext())); @@ -189,7 +201,13 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis isRefund = binding.spinnerRefundStatus.getSelectedItemPosition() == 2; } - viewModel.loadSales(reset, query, paymentMethod, storeId, isRefund); + Long customerId = null; + List customerList = viewModel.getCustomers().getValue(); + if (binding.spinnerCustomer.getSelectedItemPosition() > 0 && customerList != null && !customerList.isEmpty()) { + customerId = customerList.get(binding.spinnerCustomer.getSelectedItemPosition() - 1).getCustomerId(); + } + + viewModel.loadSales(reset, query, paymentMethod, storeId, isRefund, customerId); } @Override diff --git a/android/app/src/main/java/com/example/petstoremobile/repositories/SaleRepository.java b/android/app/src/main/java/com/example/petstoremobile/repositories/SaleRepository.java index 36ac8b30..182a7f0e 100644 --- a/android/app/src/main/java/com/example/petstoremobile/repositories/SaleRepository.java +++ b/android/app/src/main/java/com/example/petstoremobile/repositories/SaleRepository.java @@ -20,8 +20,8 @@ public class SaleRepository extends BaseRepository { this.saleApi = saleApi; } - public LiveData>> getAllSales(int page, int size, String query, String paymentMethod, Long storeId, Boolean isRefund, String sortBy) { - return executeCall(saleApi.getAllSales(page, size, query, paymentMethod, storeId, isRefund, sortBy)); + public LiveData>> getAllSales(int page, int size, String query, String paymentMethod, Long storeId, Boolean isRefund, Long customerId, String sortBy) { + return executeCall(saleApi.getAllSales(page, size, query, paymentMethod, storeId, isRefund, customerId, sortBy)); } public LiveData> getSaleById(Long id) { diff --git a/android/app/src/main/java/com/example/petstoremobile/viewmodels/AnalyticsViewModel.java b/android/app/src/main/java/com/example/petstoremobile/viewmodels/AnalyticsViewModel.java index 3e5082ec..721dad5f 100644 --- a/android/app/src/main/java/com/example/petstoremobile/viewmodels/AnalyticsViewModel.java +++ b/android/app/src/main/java/com/example/petstoremobile/viewmodels/AnalyticsViewModel.java @@ -51,7 +51,7 @@ public class AnalyticsViewModel extends ViewModel { public void loadAnalytics() { isLoading.setValue(true); errorMessage.setValue(null); - observeOnce(saleRepository.getAllSales(0, 2000, null, null, null, null, "saleDate,desc"), resource -> { + observeOnce(saleRepository.getAllSales(0, 2000, null, null, null, null, null, "saleDate,desc"), resource -> { if (resource != null) { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { cachedSales = resource.data.getContent(); diff --git a/android/app/src/main/java/com/example/petstoremobile/viewmodels/RefundViewModel.java b/android/app/src/main/java/com/example/petstoremobile/viewmodels/RefundViewModel.java index fab83dd9..ff3d069e 100644 --- a/android/app/src/main/java/com/example/petstoremobile/viewmodels/RefundViewModel.java +++ b/android/app/src/main/java/com/example/petstoremobile/viewmodels/RefundViewModel.java @@ -35,7 +35,7 @@ public class RefundViewModel extends ViewModel { } public LiveData>> loadAllSales() { - return saleRepository.getAllSales(0, 1000, null, null, null, null, "saleDate,desc"); + return saleRepository.getAllSales(0, 1000, null, null, null, null, null, "saleDate,desc"); } public void setAllSales(List sales) { diff --git a/android/app/src/main/java/com/example/petstoremobile/viewmodels/SaleListViewModel.java b/android/app/src/main/java/com/example/petstoremobile/viewmodels/SaleListViewModel.java index 7df7269a..aee0f948 100644 --- a/android/app/src/main/java/com/example/petstoremobile/viewmodels/SaleListViewModel.java +++ b/android/app/src/main/java/com/example/petstoremobile/viewmodels/SaleListViewModel.java @@ -4,9 +4,11 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; +import com.example.petstoremobile.dtos.CustomerDTO; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.SaleDTO; import com.example.petstoremobile.dtos.StoreDTO; +import com.example.petstoremobile.repositories.CustomerRepository; import com.example.petstoremobile.repositories.SaleRepository; import com.example.petstoremobile.repositories.StoreRepository; import com.example.petstoremobile.utils.Resource; @@ -24,27 +26,31 @@ import dagger.hilt.android.lifecycle.HiltViewModel; public class SaleListViewModel extends ViewModel { private final SaleRepository saleRepository; private final StoreRepository storeRepository; + private final CustomerRepository customerRepository; private final MutableLiveData> sales = new MutableLiveData<>(new ArrayList<>()); private final MutableLiveData> stores = new MutableLiveData<>(new ArrayList<>()); + private final MutableLiveData> customers = new MutableLiveData<>(new ArrayList<>()); private final MutableLiveData isLoading = new MutableLiveData<>(false); - + private int currentPage = 0; private boolean isLastPage = false; private static final int PAGE_SIZE = 20; @Inject - public SaleListViewModel(SaleRepository saleRepository, StoreRepository storeRepository) { + public SaleListViewModel(SaleRepository saleRepository, StoreRepository storeRepository, CustomerRepository customerRepository) { this.saleRepository = saleRepository; this.storeRepository = storeRepository; + this.customerRepository = customerRepository; } public LiveData> getSales() { return sales; } public LiveData> getStores() { return stores; } + public LiveData> getCustomers() { return customers; } public LiveData getIsLoading() { return isLoading; } public boolean isLastPage() { return isLastPage; } - public void loadSales(boolean reset, String query, String paymentMethod, Long storeId, Boolean isRefund) { + public void loadSales(boolean reset, String query, String paymentMethod, Long storeId, Boolean isRefund, Long customerId) { if (isLoading.getValue() != null && isLoading.getValue() && !reset) return; if (reset) { @@ -53,7 +59,7 @@ public class SaleListViewModel extends ViewModel { } isLoading.setValue(true); - observeOnce(saleRepository.getAllSales(currentPage, PAGE_SIZE, query, paymentMethod, storeId, isRefund, "saleDate,desc"), resource -> { + observeOnce(saleRepository.getAllSales(currentPage, PAGE_SIZE, query, paymentMethod, storeId, isRefund, customerId, "saleDate,desc"), resource -> { if (resource != null) { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { List currentList = reset ? new ArrayList<>() : new ArrayList<>(sales.getValue()); @@ -77,6 +83,14 @@ public class SaleListViewModel extends ViewModel { }); } + public void loadCustomers() { + observeOnce(customerRepository.getAllCustomers(0, 500), resource -> { + if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) { + customers.setValue(resource.data.getContent()); + } + }); + } + private void observeOnce(LiveData> liveData, Observer> handler) { liveData.observeForever(new Observer>() { @Override diff --git a/android/app/src/main/res/layout/fragment_sale.xml b/android/app/src/main/res/layout/fragment_sale.xml index 35ef7d23..5520badf 100644 --- a/android/app/src/main/res/layout/fragment_sale.xml +++ b/android/app/src/main/res/layout/fragment_sale.xml @@ -151,6 +151,15 @@ android:layout_marginStart="4dp"/> + + + + { ")) AND " + "(:paymentMethod IS NULL OR LOWER(s.paymentMethod) = LOWER(:paymentMethod)) AND " + "(:isRefund IS NULL OR s.isRefund = :isRefund) AND " + - "(:storeId IS NULL OR s.store.storeId = :storeId)") - Page searchSales(@Param("q") String query, @Param("paymentMethod") String paymentMethod, @Param("storeId") Long storeId, @Param("isRefund") Boolean isRefund, Pageable pageable); + "(:storeId IS NULL OR s.store.storeId = :storeId) AND " + + "(:customerId IS NULL OR s.customer.id = :customerId)") + Page searchSales(@Param("q") String query, @Param("paymentMethod") String paymentMethod, @Param("storeId") Long storeId, @Param("isRefund") Boolean isRefund, @Param("customerId") Long customerId, Pageable pageable); List findByOriginalSaleSaleId(Long originalSaleId); diff --git a/backend/src/main/java/com/petshop/backend/service/SaleService.java b/backend/src/main/java/com/petshop/backend/service/SaleService.java index dde5dc43..c1239cc3 100644 --- a/backend/src/main/java/com/petshop/backend/service/SaleService.java +++ b/backend/src/main/java/com/petshop/backend/service/SaleService.java @@ -43,8 +43,8 @@ public class SaleService { } @Transactional(readOnly = true) - public Page getAllSales(String query, String paymentMethod, Long storeId, Boolean isRefund, Pageable pageable) { - Page sales = saleRepository.searchSales(normalizeFilter(query), normalizeFilter(paymentMethod), storeId, isRefund, pageable); + public Page getAllSales(String query, String paymentMethod, Long storeId, Boolean isRefund, Long customerId, Pageable pageable) { + Page sales = saleRepository.searchSales(normalizeFilter(query), normalizeFilter(paymentMethod), storeId, isRefund, customerId, pageable); return sales.map(this::mapToResponse); }