Sales bug fix

This commit is contained in:
Alex
2026-04-10 05:56:05 -06:00
parent 9d7c577f85
commit 32e41397d4
15 changed files with 126 additions and 23 deletions

View File

@@ -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}")

View File

@@ -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<SaleItemDTO> items;
private String createdAt;
// Request fields
private Long customerId;
// Constructor for create request
public SaleDTO(Long storeId, String paymentMethod, List<SaleItemDTO> 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;

View File

@@ -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());

View File

@@ -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());

View File

@@ -73,7 +73,9 @@ public class StaffDetailFragment extends Fragment {
}
private void refreshStoreSpinner() {
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaffStore, viewModel.getStoreList().getValue(),
List<DropdownDTO> list = viewModel.getStoreList().getValue();
if (list == null) return;
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaffStore, list,
DropdownDTO::getLabel, "-- Select Store --",
preselectedStoreId, DropdownDTO::getId);
}

View File

@@ -20,8 +20,8 @@ public class SaleRepository extends BaseRepository {
this.saleApi = saleApi;
}
public LiveData<Resource<PageResponse<SaleDTO>>> 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<Resource<PageResponse<SaleDTO>>> 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<Resource<SaleDTO>> getSaleById(Long id) {

View File

@@ -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();

View File

@@ -34,7 +34,7 @@ public class RefundViewModel extends ViewModel {
}
public LiveData<Resource<PageResponse<SaleDTO>>> 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<SaleDTO> sales) {

View File

@@ -42,7 +42,7 @@ public class SaleListViewModel extends ViewModel {
public LiveData<Boolean> 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<SaleDTO> currentList = reset ? new ArrayList<>() : new ArrayList<>(sales.getValue());

View File

@@ -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<List<DropdownDTO>> storeList = new MutableLiveData<>();
private final MutableLiveData<List<DropdownDTO>> storeList = new MutableLiveData<>(new ArrayList<>());
private long employeeId = -1;
private boolean isEditing = false;

View File

@@ -117,8 +117,25 @@
android:background="@drawable/bg_spinner"
android:paddingStart="12dp"
android:paddingEnd="8dp"
android:layout_marginEnd="4dp"
android:layout_marginStart="4dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="8dp"
android:gravity="center_vertical">
<Spinner
android:id="@+id/spinnerRefundStatus"
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_weight="1"
android:background="@drawable/bg_spinner"
android:paddingStart="12dp"
android:paddingEnd="8dp"
android:layout_marginEnd="4dp"/>
<Button
android:id="@+id/btnOpenRefund"

View File

@@ -83,6 +83,40 @@
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/tvSaleStore"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="@color/text_dark"
android:layout_marginBottom="16dp"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/llCustomerInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Customer"
android:textColor="@color/text_dark"
android:textSize="12sp"
android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/tvSaleCustomer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="@color/text_dark"
android:layout_marginBottom="16dp"/>
</LinearLayout>
<TextView
android:id="@+id/tvCustomerLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Customer (Optional)"
@@ -111,6 +145,15 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/tvSalePaymentMethod"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="@color/text_dark"
android:layout_marginBottom="16dp"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/llExtraInfo"
android:layout_width="match_parent"