added filter by customer for sales to backend and android
This commit is contained in:
@@ -48,6 +48,12 @@ public class SaleAdapter extends RecyclerView.Adapter<SaleAdapter.SaleViewHolder
|
||||
|
||||
binding.tvSaleId.setText("Sale #" + (s.getSaleId() != null ? s.getSaleId() : ""));
|
||||
binding.tvSaleEmployee.setText("By: " + (s.getEmployeeName() != null ? s.getEmployeeName() : ""));
|
||||
if (s.getCustomerName() != null && !s.getCustomerName().isEmpty()) {
|
||||
binding.tvSaleCustomer.setText("Customer: " + s.getCustomerName());
|
||||
binding.tvSaleCustomer.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.tvSaleCustomer.setVisibility(View.GONE);
|
||||
}
|
||||
binding.tvSaleDate.setText(s.getSaleDate() != null ? s.getSaleDate().substring(0, Math.min(10, s.getSaleDate().length())) : "");
|
||||
binding.tvSalePayment.setText(s.getPaymentMethod() != null ? s.getPaymentMethod() : "");
|
||||
binding.tvSaleTotal.setText(s.getTotalAmount() != null ? "$" + s.getTotalAmount() : "");
|
||||
|
||||
@@ -20,6 +20,7 @@ public interface SaleApi {
|
||||
@Query("paymentMethod") String paymentMethod,
|
||||
@Query("storeId") Long storeId,
|
||||
@Query("isRefund") Boolean isRefund,
|
||||
@Query("customerId") Long customerId,
|
||||
@Query("sort") String sort);
|
||||
|
||||
@GET("api/v1/sales/{id}")
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.SaleAdapter;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.databinding.FragmentSaleBinding;
|
||||
import com.example.petstoremobile.dtos.CustomerDTO;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
@@ -57,10 +58,11 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
setupStoreFilter();
|
||||
setupPaymentMethodFilter();
|
||||
setupRefundStatusFilter();
|
||||
setupCustomerFilter();
|
||||
setupSwipeRefresh();
|
||||
setupFilterToggle();
|
||||
observeViewModel();
|
||||
|
||||
|
||||
loadSales(true);
|
||||
|
||||
UIUtils.setupHamburgerMenu(binding.btnHamburger, this);
|
||||
@@ -89,6 +91,11 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis
|
||||
StoreDTO::getStoreName, "All Stores", null, StoreDTO::getStoreId);
|
||||
});
|
||||
|
||||
viewModel.getCustomers().observe(getViewLifecycleOwner(), list -> {
|
||||
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<CustomerDTO> 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
|
||||
|
||||
@@ -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, Boolean isRefund, String sortBy) {
|
||||
return executeCall(saleApi.getAllSales(page, size, query, paymentMethod, storeId, isRefund, sortBy));
|
||||
public LiveData<Resource<PageResponse<SaleDTO>>> 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<Resource<SaleDTO>> getSaleById(Long id) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -35,7 +35,7 @@ public class RefundViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<SaleDTO>>> 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<SaleDTO> sales) {
|
||||
|
||||
@@ -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<List<SaleDTO>> sales = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<StoreDTO>> stores = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<CustomerDTO>> customers = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Boolean> 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<List<SaleDTO>> getSales() { return sales; }
|
||||
public LiveData<List<StoreDTO>> getStores() { return stores; }
|
||||
public LiveData<List<CustomerDTO>> getCustomers() { return customers; }
|
||||
public LiveData<Boolean> 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<SaleDTO> 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 <T> void observeOnce(LiveData<Resource<T>> liveData, Observer<Resource<T>> handler) {
|
||||
liveData.observeForever(new Observer<Resource<T>>() {
|
||||
@Override
|
||||
|
||||
@@ -151,6 +151,15 @@
|
||||
android:layout_marginStart="4dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerCustomer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_spinner"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
|
||||
@@ -74,6 +74,15 @@
|
||||
android:textColor="#888888"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSaleCustomer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="#888888"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSaleDate"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -28,8 +28,9 @@ public class SaleController {
|
||||
@RequestParam(required = false) String paymentMethod,
|
||||
@RequestParam(required = false) Long storeId,
|
||||
@RequestParam(required = false) Boolean isRefund,
|
||||
@RequestParam(required = false) Long customerId,
|
||||
Pageable pageable) {
|
||||
return ResponseEntity.ok(saleService.getAllSales(q, paymentMethod, storeId, isRefund, pageable));
|
||||
return ResponseEntity.ok(saleService.getAllSales(q, paymentMethod, storeId, isRefund, customerId, pageable));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
|
||||
@@ -22,8 +22,9 @@ public interface SaleRepository extends JpaRepository<Sale, Long> {
|
||||
")) 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<Sale> 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<Sale> searchSales(@Param("q") String query, @Param("paymentMethod") String paymentMethod, @Param("storeId") Long storeId, @Param("isRefund") Boolean isRefund, @Param("customerId") Long customerId, Pageable pageable);
|
||||
|
||||
List<Sale> findByOriginalSaleSaleId(Long originalSaleId);
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ public class SaleService {
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<SaleResponse> getAllSales(String query, String paymentMethod, Long storeId, Boolean isRefund, Pageable pageable) {
|
||||
Page<Sale> sales = saleRepository.searchSales(normalizeFilter(query), normalizeFilter(paymentMethod), storeId, isRefund, pageable);
|
||||
public Page<SaleResponse> getAllSales(String query, String paymentMethod, Long storeId, Boolean isRefund, Long customerId, Pageable pageable) {
|
||||
Page<Sale> sales = saleRepository.searchSales(normalizeFilter(query), normalizeFilter(paymentMethod), storeId, isRefund, customerId, pageable);
|
||||
return sales.map(this::mapToResponse);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user