From 32e41397d4d81caeafa3a3d5aeadefc48adcf375 Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Fri, 10 Apr 2026 05:56:05 -0600 Subject: [PATCH] Sales bug fix --- .../example/petstoremobile/api/SaleApi.java | 1 + .../example/petstoremobile/dtos/SaleDTO.java | 9 ++-- .../fragments/listfragments/SaleFragment.java | 20 +++++++-- .../detailfragments/SaleDetailFragment.java | 28 ++++++++++-- .../detailfragments/StaffDetailFragment.java | 4 +- .../repositories/SaleRepository.java | 4 +- .../viewmodels/AnalyticsViewModel.java | 2 +- .../viewmodels/RefundViewModel.java | 2 +- .../viewmodels/SaleListViewModel.java | 4 +- .../viewmodels/StaffDetailViewModel.java | 3 +- .../app/src/main/res/layout/fragment_sale.xml | 19 +++++++- .../main/res/layout/fragment_sale_detail.xml | 43 +++++++++++++++++++ .../backend/controller/SaleController.java | 3 +- .../backend/repository/SaleRepository.java | 3 +- .../petshop/backend/service/SaleService.java | 4 +- 15 files changed, 126 insertions(+), 23 deletions(-) diff --git a/android/app/src/main/java/com/example/petstoremobile/api/SaleApi.java b/android/app/src/main/java/com/example/petstoremobile/api/SaleApi.java index fa11f100..fd58bec4 100644 --- a/android/app/src/main/java/com/example/petstoremobile/api/SaleApi.java +++ b/android/app/src/main/java/com/example/petstoremobile/api/SaleApi.java @@ -19,6 +19,7 @@ public interface SaleApi { @Query("q") String query, @Query("paymentMethod") String paymentMethod, @Query("storeId") Long storeId, + @Query("isRefund") Boolean isRefund, @Query("sort") String sort); @GET("api/v1/sales/{id}") diff --git a/android/app/src/main/java/com/example/petstoremobile/dtos/SaleDTO.java b/android/app/src/main/java/com/example/petstoremobile/dtos/SaleDTO.java index 2c7189ee..edb2a132 100644 --- a/android/app/src/main/java/com/example/petstoremobile/dtos/SaleDTO.java +++ b/android/app/src/main/java/com/example/petstoremobile/dtos/SaleDTO.java @@ -9,6 +9,8 @@ public class SaleDTO { private String saleDate; private Long employeeId; private String employeeName; + private Long customerId; + private String customerName; private Long storeId; private String storeName; private BigDecimal totalAmount; @@ -25,9 +27,6 @@ public class SaleDTO { private List items; private String createdAt; - // Request fields - private Long customerId; - // Constructor for create request public SaleDTO(Long storeId, String paymentMethod, List items, Boolean isRefund, Long originalSaleId, Long customerId) { @@ -119,6 +118,10 @@ public class SaleDTO { return customerId; } + public String getCustomerName() { + return customerName; + } + // Nested SaleItemDTO public static class SaleItemDTO { private Long saleItemId; diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java index fef5d994..b3fd9546 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java @@ -51,6 +51,7 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis setupSearch(); setupStoreFilter(); setupPaymentMethodFilter(); + setupRefundStatusFilter(); setupSwipeRefresh(); setupFilterToggle(); observeViewModel(); @@ -75,7 +76,7 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis viewModel.getStores().observe(getViewLifecycleOwner(), list -> { SpinnerUtils.populateWhiteSpinner(requireContext(), binding.spinnerStore, list, - StoreDTO::getStoreName, "Stores", null, StoreDTO::getStoreId); + StoreDTO::getStoreName, "All Stores", null, StoreDTO::getStoreId); }); viewModel.getIsLoading().observe(getViewLifecycleOwner(), loading -> { @@ -91,7 +92,7 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis private void setupFilterToggle() { UIUtils.setupFilterToggle(binding.btnToggleFilter, binding.layoutFilter, binding.etSearchSale, - binding.spinnerPaymentMethod, binding.spinnerStore); + binding.spinnerPaymentMethod, binding.spinnerStore, binding.spinnerRefundStatus); } private void setupStoreFilter() { @@ -99,10 +100,15 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis } private void setupPaymentMethodFilter() { - String[] paymentMethods = {"Payments", "Cash", "Card"}; + String[] paymentMethods = {"All Payments", "Cash", "Card"}; SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerPaymentMethod, paymentMethods, () -> loadSales(true)); } + private void setupRefundStatusFilter() { + String[] refundStatuses = {"All Status", "Sale", "Refund"}; + SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerRefundStatus, refundStatuses, () -> loadSales(true)); + } + private void setupRecyclerView() { adapter = new SaleAdapter(saleList, this); binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext())); @@ -149,7 +155,12 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis storeId = stores.get(binding.spinnerStore.getSelectedItemPosition() - 1).getStoreId(); } - viewModel.loadSales(reset, query, paymentMethod, storeId); + Boolean isRefund = null; + if (binding.spinnerRefundStatus.getSelectedItemPosition() > 0) { + isRefund = binding.spinnerRefundStatus.getSelectedItemPosition() == 2; + } + + viewModel.loadSales(reset, query, paymentMethod, storeId, isRefund); } @Override @@ -159,6 +170,7 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis Bundle args = new Bundle(); if (sale.getSaleId() != null) { args.putLong("saleId", sale.getSaleId()); + args.putBoolean("viewOnly", true); } if (sale.getIsRefund() != null) { args.putBoolean("isRefund", sale.getIsRefund()); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java index 93a5e244..1c216ba3 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java @@ -40,7 +40,11 @@ public class SaleDetailFragment extends Fragment { observeViewModel(); handleArguments(); - if (!viewModel.isViewOnly()) { + if (viewModel.isViewOnly()) { + binding.llAddItemRow.setVisibility(View.GONE); + binding.btnSaveSale.setVisibility(View.GONE); + UIUtils.setViewsEnabled(false, binding.spinnerSaleStore, binding.spinnerSaleCustomer, binding.spinnerPaymentMethod); + } else { loadData(); setupAddItem(); } @@ -84,8 +88,9 @@ public class SaleDetailFragment extends Fragment { binding.tvSaleMode.setText("Sale #" + saleId); binding.tvSaleDetailId.setText("ID: " + saleId); - if (!a.getBoolean("isRefund", false)) { - binding.btnRefundSale.setVisibility(View.VISIBLE); + boolean isRefund = a.getBoolean("isRefund", false); + if (isRefund) { + binding.btnRefundSale.setVisibility(View.GONE); } if (viewOnly) { @@ -96,6 +101,16 @@ public class SaleDetailFragment extends Fragment { binding.spinnerPaymentMethod); binding.llAddItemRow.setVisibility(View.GONE); binding.llExtraInfo.setVisibility(View.VISIBLE); + binding.llCustomerInfo.setVisibility(View.VISIBLE); + binding.tvCustomerLabel.setVisibility(View.GONE); + binding.spinnerSaleCustomer.setVisibility(View.GONE); + binding.spinnerSaleStore.setVisibility(View.GONE); + binding.spinnerPaymentMethod.setVisibility(View.GONE); + binding.tvSaleStore.setVisibility(View.VISIBLE); + binding.tvSalePaymentMethod.setVisibility(View.VISIBLE); + + // Show refund button only if it's not already a refund + binding.btnRefundSale.setVisibility(isRefund ? View.GONE : View.VISIBLE); } loadSaleDetails(); @@ -157,6 +172,13 @@ public class SaleDetailFragment extends Fragment { binding.tvSaleChannel.setText(sale.getChannel() != null ? sale.getChannel() : "—"); binding.tvSalePoints.setText(String.valueOf(sale.getPointsEarned() != null ? sale.getPointsEarned() : 0)); + binding.tvSaleStore.setText(sale.getStoreName() != null ? sale.getStoreName() : "—"); + binding.tvSaleCustomer.setText(sale.getCustomerName() != null ? sale.getCustomerName() : "No Customer"); + binding.tvSalePaymentMethod.setText(sale.getPaymentMethod() != null ? sale.getPaymentMethod() : "—"); + + if (sale.getIsRefund() != null && sale.getIsRefund()) { + binding.btnRefundSale.setVisibility(View.GONE); + } SpinnerUtils.setSelectionByValue(binding.spinnerPaymentMethod, sale.getPaymentMethod()); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java index 3c1f81d6..acf171ac 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java @@ -73,7 +73,9 @@ public class StaffDetailFragment extends Fragment { } private void refreshStoreSpinner() { - SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaffStore, viewModel.getStoreList().getValue(), + List list = viewModel.getStoreList().getValue(); + if (list == null) return; + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaffStore, list, DropdownDTO::getLabel, "-- Select Store --", preselectedStoreId, DropdownDTO::getId); } 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 4f436163..36ac8b30 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, String sortBy) { - return executeCall(saleApi.getAllSales(page, size, query, paymentMethod, storeId, sortBy)); + 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> 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 6ebfb741..8d43d995 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 @@ -49,7 +49,7 @@ public class AnalyticsViewModel extends ViewModel { public void loadAnalytics() { isLoading.setValue(true); errorMessage.setValue(null); - saleRepository.getAllSales(0, 2000, null, null, null, "saleDate,desc").observeForever(resource -> { + saleRepository.getAllSales(0, 2000, null, null, null, null, "saleDate,desc").observeForever(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 d2b13732..222a1fa4 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 @@ -34,7 +34,7 @@ public class RefundViewModel extends ViewModel { } public LiveData>> loadAllSales() { - return saleRepository.getAllSales(0, 1000, null, null, null, "saleDate,desc"); + return saleRepository.getAllSales(0, 1000, 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 a364a7d8..297d987f 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 @@ -42,7 +42,7 @@ public class SaleListViewModel extends ViewModel { public LiveData getIsLoading() { return isLoading; } public boolean isLastPage() { return isLastPage; } - public void loadSales(boolean reset, String query, String paymentMethod, Long storeId) { + public void loadSales(boolean reset, String query, String paymentMethod, Long storeId, Boolean isRefund) { if (isLoading.getValue() != null && isLoading.getValue() && !reset) return; if (reset) { @@ -51,7 +51,7 @@ public class SaleListViewModel extends ViewModel { } isLoading.setValue(true); - saleRepository.getAllSales(currentPage, PAGE_SIZE, query, paymentMethod, storeId, "saleDate,desc").observeForever(resource -> { + saleRepository.getAllSales(currentPage, PAGE_SIZE, query, paymentMethod, storeId, isRefund, "saleDate,desc").observeForever(resource -> { if (resource != null) { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { List currentList = reset ? new ArrayList<>() : new ArrayList<>(sales.getValue()); diff --git a/android/app/src/main/java/com/example/petstoremobile/viewmodels/StaffDetailViewModel.java b/android/app/src/main/java/com/example/petstoremobile/viewmodels/StaffDetailViewModel.java index dffe47c3..cbee3bc3 100644 --- a/android/app/src/main/java/com/example/petstoremobile/viewmodels/StaffDetailViewModel.java +++ b/android/app/src/main/java/com/example/petstoremobile/viewmodels/StaffDetailViewModel.java @@ -10,6 +10,7 @@ import com.example.petstoremobile.repositories.EmployeeRepository; import com.example.petstoremobile.repositories.StoreRepository; import com.example.petstoremobile.utils.Resource; +import java.util.ArrayList; import java.util.List; import javax.inject.Inject; @@ -20,7 +21,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel; public class StaffDetailViewModel extends ViewModel { private final EmployeeRepository repository; private final StoreRepository storeRepository; - private final MutableLiveData> storeList = new MutableLiveData<>(); + private final MutableLiveData> storeList = new MutableLiveData<>(new ArrayList<>()); private long employeeId = -1; private boolean isEditing = false; diff --git a/android/app/src/main/res/layout/fragment_sale.xml b/android/app/src/main/res/layout/fragment_sale.xml index ff3a693c..35ef7d23 100644 --- a/android/app/src/main/res/layout/fragment_sale.xml +++ b/android/app/src/main/res/layout/fragment_sale.xml @@ -117,8 +117,25 @@ android:background="@drawable/bg_spinner" android:paddingStart="12dp" android:paddingEnd="8dp" - android:layout_marginEnd="4dp" android:layout_marginStart="4dp"/> + + + + +