created viewmodels for detailFragments
This commit is contained in:
@@ -16,11 +16,7 @@ import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.AdoptionViewModel;
|
||||
import com.example.petstoremobile.viewmodels.CustomerViewModel;
|
||||
import com.example.petstoremobile.viewmodels.PetViewModel;
|
||||
import com.example.petstoremobile.viewmodels.StoreViewModel;
|
||||
import com.example.petstoremobile.viewmodels.UserViewModel;
|
||||
import com.example.petstoremobile.viewmodels.AdoptionDetailViewModel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
@@ -34,35 +30,19 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class AdoptionDetailFragment extends Fragment {
|
||||
|
||||
private FragmentAdoptionDetailBinding binding;
|
||||
private AdoptionDetailViewModel viewModel;
|
||||
|
||||
private long adoptionId = -1;
|
||||
private boolean isEditing = false;
|
||||
private long preselectedPetId = -1;
|
||||
private long preselectedCustomerId = -1;
|
||||
private long preselectedStoreId = -1;
|
||||
private long preselectedEmployeeId = -1;
|
||||
|
||||
private List<DropdownDTO> petList = new ArrayList<>();
|
||||
private List<DropdownDTO> customerList = new ArrayList<>();
|
||||
private List<DropdownDTO> storeList = new ArrayList<>();
|
||||
private List<DropdownDTO> employeeList = new ArrayList<>();
|
||||
|
||||
private final String[] STATUSES = {"Pending", "Completed", "Cancelled"};
|
||||
|
||||
private AdoptionViewModel adoptionViewModel;
|
||||
private PetViewModel petViewModel;
|
||||
private CustomerViewModel customerViewModel;
|
||||
private StoreViewModel storeViewModel;
|
||||
private UserViewModel userViewModel;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
adoptionViewModel = new ViewModelProvider(this).get(AdoptionViewModel.class);
|
||||
petViewModel = new ViewModelProvider(this).get(PetViewModel.class);
|
||||
customerViewModel = new ViewModelProvider(this).get(CustomerViewModel.class);
|
||||
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
|
||||
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(AdoptionDetailViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,6 +57,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setupSpinners();
|
||||
setupDatePicker();
|
||||
observeViewModel();
|
||||
loadSpinnersData();
|
||||
handleArguments();
|
||||
|
||||
@@ -85,29 +66,31 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
binding.btnDeleteAdoption.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getPetList().observe(getViewLifecycleOwner(), list -> refreshPetSpinner());
|
||||
viewModel.getCustomerList().observe(getViewLifecycleOwner(), list -> refreshCustomerSpinner());
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner());
|
||||
viewModel.getEmployeeList().observe(getViewLifecycleOwner(), list -> refreshEmployeeSpinner());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the spinner for adoption status.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAdoptionStatus, STATUSES);
|
||||
|
||||
// Pet spinner disabled by default until customer is selected
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerAdoptionPet);
|
||||
|
||||
// Listener to enable pet spinner based on customer selection
|
||||
binding.spinnerAdoptionCustomer.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position > 0) {
|
||||
UIUtils.setViewsEnabled(true, binding.spinnerAdoptionPet);
|
||||
} else {
|
||||
if (!isEditing) {
|
||||
if (!viewModel.isEditing()) {
|
||||
binding.spinnerAdoptionPet.setSelection(0);
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerAdoptionPet);
|
||||
}
|
||||
@@ -117,27 +100,21 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
|
||||
// Listener to load employees based on selected store
|
||||
binding.spinnerAdoptionStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position > 0 && position <= storeList.size()) {
|
||||
DropdownDTO selectedStore = storeList.get(position - 1);
|
||||
if (position > 0 && viewModel.getStoreList().getValue() != null && position <= viewModel.getStoreList().getValue().size()) {
|
||||
DropdownDTO selectedStore = viewModel.getStoreList().getValue().get(position - 1);
|
||||
loadEmployees(selectedStore.getId());
|
||||
} else {
|
||||
employeeList.clear();
|
||||
refreshEmployeeSpinner();
|
||||
viewModel.setEmployeeList(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the date picker dialog for the adoption date field.
|
||||
*/
|
||||
private void setupDatePicker() {
|
||||
binding.etAdoptionDate.setOnClickListener(v -> {
|
||||
Calendar c = Calendar.getInstance();
|
||||
@@ -150,129 +127,77 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches required data for spinners from the backend.
|
||||
*/
|
||||
private void loadSpinnersData() {
|
||||
loadPets();
|
||||
loadCustomers();
|
||||
loadStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of pets from the API.
|
||||
*/
|
||||
private void loadPets() {
|
||||
petViewModel.getAdoptionPets().observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadPets().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
petList = resource.data;
|
||||
refreshPetSpinner();
|
||||
viewModel.setPetList(resource.data);
|
||||
}
|
||||
});
|
||||
viewModel.loadCustomers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setCustomerList(resource.data);
|
||||
}
|
||||
});
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setStoreList(resource.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the pet selection spinner with data.
|
||||
*/
|
||||
private void refreshPetSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionPet, petList,
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionPet, viewModel.getPetList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Pet --",
|
||||
preselectedPetId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of customers from the API.
|
||||
*/
|
||||
private void loadCustomers() {
|
||||
customerViewModel.getCustomerDropdowns().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
customerList = resource.data;
|
||||
refreshCustomerSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the customer selection spinner with data.
|
||||
*/
|
||||
private void refreshCustomerSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionCustomer, customerList,
|
||||
DropdownDTO::getLabel,
|
||||
"-- Select Customer --",
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionCustomer, viewModel.getCustomerList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Customer --",
|
||||
preselectedCustomerId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of stores from the API.
|
||||
*/
|
||||
private void loadStores() {
|
||||
storeViewModel.getStoreDropdowns().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
storeList = resource.data;
|
||||
refreshStoreSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the store selection spinner with data.
|
||||
*/
|
||||
private void refreshStoreSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionStore, storeList,
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionStore, viewModel.getStoreList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Store --",
|
||||
preselectedStoreId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of employees for a specific store.
|
||||
*/
|
||||
private void loadEmployees(Long storeId) {
|
||||
storeViewModel.getStoreEmployees(storeId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadEmployees(storeId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
employeeList = resource.data;
|
||||
refreshEmployeeSpinner();
|
||||
viewModel.setEmployeeList(resource.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the employee selection spinner with data.
|
||||
*/
|
||||
private void refreshEmployeeSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionEmployee, employeeList,
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionEmployee, viewModel.getEmployeeList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Staff --",
|
||||
preselectedEmployeeId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments to determine if the fragment is in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("adoptionId")) {
|
||||
isEditing = true;
|
||||
adoptionId = a.getLong("adoptionId");
|
||||
long adoptionId = a.getLong("adoptionId");
|
||||
viewModel.setAdoptionId(adoptionId);
|
||||
binding.tvAdoptionMode.setText("Edit Adoption");
|
||||
binding.tvAdoptionId.setText("ID: " + adoptionId);
|
||||
binding.tvAdoptionId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeleteAdoption.setVisibility(View.VISIBLE);
|
||||
loadAdoptionData();
|
||||
} else {
|
||||
isEditing = false;
|
||||
viewModel.setAdoptionId(-1);
|
||||
binding.tvAdoptionMode.setText("Add Adoption");
|
||||
binding.btnDeleteAdoption.setVisibility(View.GONE);
|
||||
binding.tvAdoptionId.setVisibility(View.GONE);
|
||||
|
||||
// Explicitly disable in add mode
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerAdoptionPet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific adoption details from the backend using the ID.
|
||||
*/
|
||||
private void loadAdoptionData() {
|
||||
adoptionViewModel.getAdoptionById(adoptionId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
AdoptionDTO a = resource.data;
|
||||
@@ -289,7 +214,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
refreshCustomerSpinner();
|
||||
refreshStoreSpinner();
|
||||
|
||||
// In edit mode, if a customer is already set, ensure pet spinner is enabled
|
||||
if (preselectedCustomerId != -1) {
|
||||
UIUtils.setViewsEnabled(true, binding.spinnerAdoptionPet);
|
||||
}
|
||||
@@ -299,9 +223,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the adoption request to the backend.
|
||||
*/
|
||||
private void saveAdoption() {
|
||||
if (binding.spinnerAdoptionCustomer.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a customer", Toast.LENGTH_SHORT).show(); return;
|
||||
@@ -328,13 +249,13 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
DropdownDTO customer = customerList.get(binding.spinnerAdoptionCustomer.getSelectedItemPosition() - 1);
|
||||
DropdownDTO pet = petList.get(binding.spinnerAdoptionPet.getSelectedItemPosition() - 1);
|
||||
DropdownDTO store = storeList.get(binding.spinnerAdoptionStore.getSelectedItemPosition() - 1);
|
||||
DropdownDTO customer = viewModel.getCustomerList().getValue().get(binding.spinnerAdoptionCustomer.getSelectedItemPosition() - 1);
|
||||
DropdownDTO pet = viewModel.getPetList().getValue().get(binding.spinnerAdoptionPet.getSelectedItemPosition() - 1);
|
||||
DropdownDTO store = viewModel.getStoreList().getValue().get(binding.spinnerAdoptionStore.getSelectedItemPosition() - 1);
|
||||
|
||||
Long employeeId = null;
|
||||
if (binding.spinnerAdoptionEmployee.getSelectedItemPosition() > 0) {
|
||||
employeeId = employeeList.get(binding.spinnerAdoptionEmployee.getSelectedItemPosition() - 1).getId();
|
||||
employeeId = viewModel.getEmployeeList().getValue().get(binding.spinnerAdoptionEmployee.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
String status = STATUSES[binding.spinnerAdoptionStatus.getSelectedItemPosition()];
|
||||
@@ -349,33 +270,19 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
fee
|
||||
);
|
||||
|
||||
if (isEditing) {
|
||||
adoptionViewModel.updateAdoption(adoptionId, dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Updated", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
adoptionViewModel.createAdoption(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Saved", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
viewModel.saveAdoption(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Updated" : "Saved", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog before deleting an adoption request.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Adoption", () ->
|
||||
adoptionViewModel.deleteAdoption(adoptionId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Deleted", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
@@ -385,9 +292,6 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous fragment.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
@@ -43,37 +43,28 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
private final Integer[] HOURS = {9, 10, 11, 12, 13, 14, 15, 16, 17};
|
||||
private final Integer[] MINUTES = {0, 15, 30, 45};
|
||||
|
||||
private AppointmentDetailViewModel appointmentViewModel;
|
||||
private AppointmentDetailViewModel viewModel;
|
||||
private boolean isUpdatingUI = false;
|
||||
|
||||
/**
|
||||
* Called when the fragment is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
appointmentViewModel = new ViewModelProvider(this).get(AppointmentDetailViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(AppointmentDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the view hierarchy with the fragment.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately after onCreateView has returned.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setupSpinners();
|
||||
setupDatePicker();
|
||||
observeViewModel();
|
||||
appointmentViewModel.loadInitialFormData();
|
||||
viewModel.loadInitialFormData();
|
||||
handleArguments();
|
||||
|
||||
binding.btnApptBack.setOnClickListener(v -> navigateBack());
|
||||
@@ -81,110 +72,83 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the view previously created by onCreateView has been detached.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the adapters and listeners for all spinners.
|
||||
*/
|
||||
private void setupSpinners() {
|
||||
//Status Spinner is empty by default the date determines whats in here
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, new String[]{});
|
||||
|
||||
// Set up hour and minute spinners
|
||||
String[] hours = new String[HOURS.length];
|
||||
for (int i = 0; i < HOURS.length; i++)
|
||||
hours[i] = DateTimeUtils.formatTime(HOURS[i], 0);
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerHour, hours);
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerMinute, new String[]{"00", "15", "30", "45"});
|
||||
|
||||
// Pet and Staff spinners disabled by until parent selection
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerPet, binding.spinnerStaff);
|
||||
|
||||
// Listener to notify ViewModel of customer selection
|
||||
binding.spinnerCustomer.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
appointmentViewModel.onCustomerSelected(position);
|
||||
viewModel.onCustomerSelected(position);
|
||||
}
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
|
||||
// Listener to notify ViewModel of store selection
|
||||
binding.spinnerStore.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
appointmentViewModel.onStoreSelected(position);
|
||||
viewModel.onStoreSelected(position);
|
||||
}
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
|
||||
// Listeners for other selections
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerService, p -> appointmentViewModel.onServiceSelected(p));
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerPet, p -> appointmentViewModel.onPetSelected(p));
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerStaff, p -> appointmentViewModel.onStaffSelected(p));
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerService, p -> viewModel.onServiceSelected(p));
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerPet, p -> viewModel.onPetSelected(p));
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerStaff, p -> viewModel.onStaffSelected(p));
|
||||
|
||||
// Listeners for time changes
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerHour, p -> notifyDateTimeStatusChange());
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerMinute, p -> notifyDateTimeStatusChange());
|
||||
|
||||
// Listener to notify ViewModel of status selection
|
||||
SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAppointmentStatus, p -> notifyDateTimeStatusChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the date picker dialog for the appointment date field.
|
||||
*/
|
||||
private void setupDatePicker() {
|
||||
binding.etAppointmentDate.setOnClickListener(v ->
|
||||
UIUtils.showDatePicker(requireContext(), binding.etAppointmentDate, this::notifyDateTimeStatusChange));
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the ViewModel for UI state and list updates.
|
||||
*/
|
||||
private void observeViewModel() {
|
||||
appointmentViewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);
|
||||
|
||||
// Populate spinners when data arrives
|
||||
appointmentViewModel.getCustomers().observe(getViewLifecycleOwner(), list ->
|
||||
viewModel.getCustomers().observe(getViewLifecycleOwner(), list ->
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerCustomer, list, DropdownDTO::getLabel, "-- Select Customer --", preselectedCustomerId, DropdownDTO::getId));
|
||||
|
||||
appointmentViewModel.getStores().observe(getViewLifecycleOwner(), list ->
|
||||
viewModel.getStores().observe(getViewLifecycleOwner(), list ->
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStore, list, DropdownDTO::getLabel, "-- Select Store --", preselectedStoreId, DropdownDTO::getId));
|
||||
|
||||
appointmentViewModel.getServices().observe(getViewLifecycleOwner(), list ->
|
||||
viewModel.getServices().observe(getViewLifecycleOwner(), list ->
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerService, list, ServiceDTO::getServiceName, "-- Select Service --", preselectedServiceId, ServiceDTO::getServiceId));
|
||||
|
||||
appointmentViewModel.getCustomerPets().observe(getViewLifecycleOwner(), list ->
|
||||
viewModel.getCustomerPets().observe(getViewLifecycleOwner(), list ->
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPet, list, DropdownDTO::getLabel, "-- Select Pet --", preselectedPetId, DropdownDTO::getId));
|
||||
|
||||
appointmentViewModel.getStoreEmployees().observe(getViewLifecycleOwner(), list ->
|
||||
viewModel.getStoreEmployees().observe(getViewLifecycleOwner(), list ->
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaff, list, DropdownDTO::getLabel, "-- Select Staff --", preselectedStaffId, DropdownDTO::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the ViewState provided by the ViewModel to the UI components.
|
||||
*/
|
||||
private void applyViewState(AppointmentDetailViewModel.ViewState state) {
|
||||
isUpdatingUI = true;
|
||||
|
||||
// Mode specific UI
|
||||
binding.tvApptMode.setText(state.isEditing ? "Edit Appointment" : "Add Appointment");
|
||||
binding.tvAppointmentId.setText(DateTimeUtils.formatId(appointmentViewModel.getAppointmentId()));
|
||||
binding.tvAppointmentId.setText(DateTimeUtils.formatId(viewModel.getAppointmentId()));
|
||||
binding.tvAppointmentId.setVisibility(state.isEditing ? View.VISIBLE : View.GONE);
|
||||
binding.btnDeleteAppointment.setVisibility(state.isDeleteVisible ? View.VISIBLE : View.GONE);
|
||||
binding.btnSaveAppointment.setVisibility(state.isSaveVisible ? View.VISIBLE : View.GONE);
|
||||
|
||||
// Enabling/Disabling Views and Labels
|
||||
UIUtils.setFieldEnabled(state.isCustomerEnabled, binding.spinnerCustomer, binding.tvLabelCustomer);
|
||||
UIUtils.setFieldEnabled(state.isStoreEnabled, binding.spinnerStore, binding.tvLabelStore);
|
||||
UIUtils.setFieldEnabled(state.isPetEnabled, binding.spinnerPet, binding.tvLabelPet);
|
||||
@@ -195,7 +159,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
UIUtils.setViewsEnabled(state.isTimeEnabled, binding.spinnerMinute);
|
||||
UIUtils.setViewsEnabled(state.isStatusEnabled, binding.spinnerAppointmentStatus);
|
||||
|
||||
// Update status options
|
||||
Object selected = binding.spinnerAppointmentStatus.getSelectedItem();
|
||||
String current = selected != null ? selected.toString() : "";
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, state.availableStatuses);
|
||||
@@ -204,9 +167,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
isUpdatingUI = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the ViewModel that the date, time, or status has changed.
|
||||
*/
|
||||
private void notifyDateTimeStatusChange() {
|
||||
if (isUpdatingUI) return;
|
||||
|
||||
@@ -214,27 +174,21 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
String time = buildTimeString();
|
||||
Object selected = binding.spinnerAppointmentStatus.getSelectedItem();
|
||||
String status = selected != null ? selected.toString() : "";
|
||||
appointmentViewModel.onDateOrTimeChanged(date, time, status);
|
||||
viewModel.onDateOrTimeChanged(date, time, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments to determine if the fragment is in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("appointmentId")) {
|
||||
appointmentViewModel.setAppointmentId(a.getLong("appointmentId"));
|
||||
viewModel.setAppointmentId(a.getLong("appointmentId"));
|
||||
loadAppointmentData();
|
||||
} else {
|
||||
appointmentViewModel.setAppointmentId(-1);
|
||||
viewModel.setAppointmentId(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific appointment details from the backend using the ID.
|
||||
*/
|
||||
private void loadAppointmentData() {
|
||||
appointmentViewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null || resource.status != Resource.Status.SUCCESS || resource.data == null) return;
|
||||
AppointmentDTO a = resource.data;
|
||||
preselectedPetId = a.getPetId() != null ? a.getPetId() : -1;
|
||||
@@ -254,9 +208,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the appointment to the backend.
|
||||
*/
|
||||
private void saveAppointment() {
|
||||
if (!validateRequiredFields()) return;
|
||||
|
||||
@@ -264,14 +215,14 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
String time = buildTimeString();
|
||||
String status = binding.spinnerAppointmentStatus.getSelectedItem().toString().toUpperCase();
|
||||
|
||||
if (!appointmentViewModel.isValidFutureBooking(status, date, time)) {
|
||||
if (!viewModel.isValidFutureBooking(status, date, time)) {
|
||||
DialogUtils.showInfoDialog(requireContext(), "Invalid Time", "Booked appointments must be in the future.");
|
||||
return;
|
||||
}
|
||||
|
||||
appointmentViewModel.saveAppointment(date, time, status).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.saveAppointment(date, time, status).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
AppointmentDetailViewModel.ViewState state = appointmentViewModel.getViewState().getValue();
|
||||
AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
|
||||
String message = (state != null && state.isEditing) ? "Updated" : "Saved";
|
||||
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
@@ -281,9 +232,6 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that all required fields are selected.
|
||||
*/
|
||||
private boolean validateRequiredFields() {
|
||||
if (binding.spinnerCustomer.getSelectedItemPosition() == 0) return UIUtils.showToast(getContext(), "Select a customer");
|
||||
if (binding.spinnerStore.getSelectedItemPosition() == 0) return UIUtils.showToast(getContext(), "Select a store");
|
||||
@@ -293,24 +241,15 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a time string from the hour and minute spinners.
|
||||
*/
|
||||
private String buildTimeString() {
|
||||
return DateTimeUtils.formatTime(HOURS[binding.spinnerHour.getSelectedItemPosition()], MINUTES[binding.spinnerMinute.getSelectedItemPosition()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors that occur during the saving process.
|
||||
*/
|
||||
private void handleSaveError(String errorMessage) {
|
||||
if (errorMessage != null && errorMessage.toLowerCase().contains("not available")) showNoAvailabilityDialog();
|
||||
else Toast.makeText(getContext(), errorMessage != null ? errorMessage : "Error saving", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a specialized dialog when a time slot is not available.
|
||||
*/
|
||||
private void showNoAvailabilityDialog() {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle("No Availability")
|
||||
@@ -319,26 +258,17 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
.setNegativeButton("Cancel Booking", (d, w) -> navigateBack()).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog and handles the deletion of an appointment.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Appointment", () ->
|
||||
appointmentViewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) navigateBack();
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a time string and sets the hour and minute spinners.
|
||||
*/
|
||||
private void parseAndSetTimeSpinners(String time) {
|
||||
int[] parsedTime = DateTimeUtils.parseTimeString(time);
|
||||
if (parsedTime == null) return;
|
||||
|
||||
@@ -17,16 +17,10 @@ import com.example.petstoremobile.databinding.FragmentInventoryDetailBinding;
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.viewmodels.InventoryViewModel;
|
||||
import com.example.petstoremobile.viewmodels.ProductViewModel;
|
||||
import com.example.petstoremobile.viewmodels.StoreViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.example.petstoremobile.viewmodels.InventoryDetailViewModel;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@@ -37,33 +31,17 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class InventoryDetailFragment extends Fragment {
|
||||
|
||||
private FragmentInventoryDetailBinding binding;
|
||||
private InventoryDetailViewModel viewModel;
|
||||
|
||||
private InventoryViewModel inventoryViewModel;
|
||||
private ProductViewModel productViewModel;
|
||||
private StoreViewModel storeViewModel;
|
||||
|
||||
private boolean isEditing = false;
|
||||
private long inventoryId = -1;
|
||||
private long preselectedStoreId = -1;
|
||||
private long preselectedProductId = -1;
|
||||
|
||||
private List<DropdownDTO> storeList = new ArrayList<>();
|
||||
private List<ProductDTO> productList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Initializes the view models.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
inventoryViewModel = new ViewModelProvider(this).get(InventoryViewModel.class);
|
||||
productViewModel = new ViewModelProvider(this).get(ProductViewModel.class);
|
||||
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(InventoryDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -71,13 +49,11 @@ public class InventoryDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
observeViewModel();
|
||||
loadSpinnersData();
|
||||
handleArguments();
|
||||
|
||||
@@ -86,64 +62,47 @@ public class InventoryDetailFragment extends Fragment {
|
||||
binding.btnDeleteInventory.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> refreshStoreSpinner());
|
||||
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches required data for spinners from the backend.
|
||||
*/
|
||||
private void loadSpinnersData() {
|
||||
loadStores();
|
||||
loadProducts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of stores for the spinner.
|
||||
*/
|
||||
private void loadStores() {
|
||||
storeViewModel.getStoreDropdowns().observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
storeList = resource.data;
|
||||
refreshStoreSpinner();
|
||||
viewModel.setStoreList(resource.data);
|
||||
}
|
||||
});
|
||||
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setProductList(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshStoreSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryStore, storeList,
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryStore, viewModel.getStoreList().getValue(),
|
||||
DropdownDTO::getLabel, "-- Select Store --",
|
||||
preselectedStoreId, DropdownDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of products for the spinner.
|
||||
*/
|
||||
private void loadProducts() {
|
||||
productViewModel.getAllProducts(null, null, 0, 500, "prodName").observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
productList = resource.data.getContent();
|
||||
refreshProductSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshProductSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryProduct, productList,
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerInventoryProduct, viewModel.getProductList().getValue(),
|
||||
ProductDTO::getProdName, "-- Select Product --",
|
||||
preselectedProductId, ProductDTO::getProdId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fragment arguments to determine if we are in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("inventoryId")) {
|
||||
isEditing = true;
|
||||
inventoryId = args.getLong("inventoryId");
|
||||
long inventoryId = args.getLong("inventoryId");
|
||||
viewModel.setInventoryId(inventoryId);
|
||||
|
||||
binding.tvInventoryMode.setText("Edit Inventory");
|
||||
binding.tvInventoryId.setText("Inventory ID: " + inventoryId);
|
||||
@@ -153,7 +112,7 @@ public class InventoryDetailFragment extends Fragment {
|
||||
|
||||
loadInventoryData();
|
||||
} else {
|
||||
isEditing = false;
|
||||
viewModel.setInventoryId(-1);
|
||||
binding.tvInventoryMode.setText("Add Inventory");
|
||||
binding.tvInventoryId.setVisibility(View.GONE);
|
||||
binding.btnDeleteInventory.setVisibility(View.GONE);
|
||||
@@ -161,11 +120,8 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads existing inventory data from the backend.
|
||||
*/
|
||||
private void loadInventoryData() {
|
||||
inventoryViewModel.getInventoryById(inventoryId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
InventoryDTO inv = resource.data;
|
||||
@@ -181,9 +137,6 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the current inventory item details to the backend.
|
||||
*/
|
||||
private void saveInventory() {
|
||||
if (binding.spinnerInventoryStore.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Please select a store", Toast.LENGTH_SHORT).show();
|
||||
@@ -200,38 +153,23 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
int quantity = Integer.parseInt(binding.etQuantity.getText().toString().trim());
|
||||
DropdownDTO store = storeList.get(binding.spinnerInventoryStore.getSelectedItemPosition() - 1);
|
||||
ProductDTO product = productList.get(binding.spinnerInventoryProduct.getSelectedItemPosition() - 1);
|
||||
DropdownDTO store = viewModel.getStoreList().getValue().get(binding.spinnerInventoryStore.getSelectedItemPosition() - 1);
|
||||
ProductDTO product = viewModel.getProductList().getValue().get(binding.spinnerInventoryProduct.getSelectedItemPosition() - 1);
|
||||
|
||||
InventoryDTO request = new InventoryDTO(product.getProdId(), store.getId(), quantity);
|
||||
setButtonsEnabled(false);
|
||||
|
||||
if (isEditing) {
|
||||
inventoryViewModel.updateInventory(inventoryId, request).observe(getViewLifecycleOwner(), resource -> {
|
||||
setButtonsEnabled(true);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Inventory updated", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
inventoryViewModel.createInventory(request).observe(getViewLifecycleOwner(), resource -> {
|
||||
setButtonsEnabled(true);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Inventory created", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
viewModel.saveInventory(request).observe(getViewLifecycleOwner(), resource -> {
|
||||
setButtonsEnabled(true);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Inventory updated" : "Inventory created", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog before deleting an inventory item.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete inventory item?")
|
||||
@@ -241,12 +179,9 @@ public class InventoryDetailFragment extends Fragment {
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete the inventory item.
|
||||
*/
|
||||
private void deleteInventory() {
|
||||
setButtonsEnabled(false);
|
||||
inventoryViewModel.deleteInventory(inventoryId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
setButtonsEnabled(true);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Inventory deleted", Toast.LENGTH_SHORT).show();
|
||||
@@ -257,16 +192,10 @@ public class InventoryDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous fragment.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables action buttons.
|
||||
*/
|
||||
private void setButtonsEnabled(boolean enabled) {
|
||||
binding.btnSaveInventory.setEnabled(enabled);
|
||||
binding.btnDeleteInventory.setEnabled(enabled);
|
||||
|
||||
@@ -26,12 +26,8 @@ import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.CustomerViewModel;
|
||||
import com.example.petstoremobile.viewmodels.PetViewModel;
|
||||
import com.example.petstoremobile.viewmodels.StoreViewModel;
|
||||
import com.example.petstoremobile.viewmodels.PetDetailViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
@@ -43,23 +39,15 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class PetDetailFragment extends Fragment {
|
||||
|
||||
private FragmentPetDetailBinding binding;
|
||||
private long petId;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private PetViewModel viewModel;
|
||||
private CustomerViewModel customerViewModel;
|
||||
private StoreViewModel storeViewModel;
|
||||
private List<DropdownDTO> customerList = new ArrayList<>();
|
||||
private List<DropdownDTO> storeList = new ArrayList<>();
|
||||
private PetDetailViewModel viewModel;
|
||||
|
||||
private Long selectedCustomerId = null;
|
||||
private Long selectedStoreId = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PetViewModel.class);
|
||||
customerViewModel = new ViewModelProvider(this).get(CustomerViewModel.class);
|
||||
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(PetDetailViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,8 +62,7 @@ public class PetDetailFragment extends Fragment {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
setupSpinner();
|
||||
loadCustomers();
|
||||
loadStores();
|
||||
observeViewModel();
|
||||
handleArguments();
|
||||
|
||||
//set button click listeners
|
||||
@@ -84,6 +71,23 @@ public class PetDetailFragment extends Fragment {
|
||||
binding.btnDeletePet.setOnClickListener(v -> deletePet());
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getCustomerList().observe(getViewLifecycleOwner(), list -> updateCustomerSpinnerSelection());
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> updateStoreSpinnerSelection());
|
||||
|
||||
viewModel.loadCustomers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setCustomerList(resource.data);
|
||||
}
|
||||
});
|
||||
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setStoreList(resource.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -113,14 +117,14 @@ public class PetDetailFragment extends Fragment {
|
||||
Long customerId = null;
|
||||
int customerPos = binding.spinnerCustomer.getSelectedItemPosition();
|
||||
if (customerPos > 0) { // 0 means no customer for pet
|
||||
customerId = customerList.get(customerPos - 1).getId();
|
||||
customerId = viewModel.getCustomerList().getValue().get(customerPos - 1).getId();
|
||||
}
|
||||
|
||||
// Get selected store
|
||||
Long storeId = null;
|
||||
int storePos = binding.spinnerStore.getSelectedItemPosition();
|
||||
if (storePos > 0) {
|
||||
storeId = storeList.get(storePos - 1).getId();
|
||||
storeId = viewModel.getStoreList().getValue().get(storePos - 1).getId();
|
||||
}
|
||||
|
||||
// Validation: If status is Available, a store must be selected
|
||||
@@ -150,31 +154,20 @@ public class PetDetailFragment extends Fragment {
|
||||
petDTO.setCustomerId(customerId);
|
||||
petDTO.setStoreId(storeId);
|
||||
|
||||
//check if the pet is being edited or added
|
||||
if (isEditing) {
|
||||
// Update existing pet
|
||||
petDTO.setPetId(petId);
|
||||
viewModel.updatePet(petId, petDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "UPDATED", (int) petId);
|
||||
viewModel.savePet(petDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (viewModel.isEditing()) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "UPDATED", (int) viewModel.getPetId());
|
||||
Toast.makeText(getContext(), "Pet updated successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateToPetList();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Add new pet
|
||||
viewModel.createPet(petDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
} else {
|
||||
ActivityLogger.log(requireContext(), "Added new Pet: " + name);
|
||||
Toast.makeText(getContext(), "Pet added successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateToPetList();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
navigateToPetList();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,9 +175,9 @@ public class PetDetailFragment extends Fragment {
|
||||
*/
|
||||
private void deletePet() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Pet", () ->
|
||||
viewModel.deletePet(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deletePet().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "DELETED", (int) petId);
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "DELETED", (int) viewModel.getPetId());
|
||||
Toast.makeText(getContext(), "Pet deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateToPetList();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -211,30 +204,23 @@ public class PetDetailFragment extends Fragment {
|
||||
* Handles arguments passed to the fragment to determine if it's in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
// Pet is being edited if the bundle contains a petId
|
||||
if (getArguments() != null && getArguments().containsKey("petId")) {
|
||||
// Get pet data from arguments and populate fields
|
||||
isEditing = true;
|
||||
petId = getArguments().getLong("petId");
|
||||
long petId = getArguments().getLong("petId");
|
||||
viewModel.setPetId(petId);
|
||||
binding.tvMode.setText("Edit Pet");
|
||||
binding.tvPetId.setText("ID: " + petId);
|
||||
binding.tvPetId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeletePet.setVisibility(View.VISIBLE);
|
||||
|
||||
// Disable species and breed fields in edit mode
|
||||
UIUtils.setViewsEnabled(false, binding.etPetSpecies, binding.etPetBreed);
|
||||
|
||||
loadPetData();
|
||||
} else {
|
||||
// Pet is being added
|
||||
// Set default values for add a new pet
|
||||
isEditing = false;
|
||||
viewModel.setPetId(-1);
|
||||
binding.tvMode.setText("Add Pet");
|
||||
binding.tvPetId.setVisibility(View.GONE);
|
||||
binding.btnDeletePet.setVisibility(View.GONE);
|
||||
binding.btnSavePet.setText("Add");
|
||||
|
||||
// Enable species and breed fields in edit mode
|
||||
UIUtils.setViewsEnabled(true, binding.etPetSpecies, binding.etPetBreed);
|
||||
}
|
||||
}
|
||||
@@ -243,7 +229,7 @@ public class PetDetailFragment extends Fragment {
|
||||
* Fetches specific pet details from the backend using the ID.
|
||||
*/
|
||||
private void loadPetData() {
|
||||
viewModel.getPetById(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadPet().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PetDTO p = resource.data;
|
||||
@@ -267,30 +253,6 @@ public class PetDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of customers and populates the spinner.
|
||||
*/
|
||||
private void loadCustomers() {
|
||||
customerViewModel.getCustomerDropdowns().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
customerList = resource.data;
|
||||
updateCustomerSpinnerSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of stores and populates the spinner.
|
||||
*/
|
||||
private void loadStores() {
|
||||
storeViewModel.getStoreDropdowns().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
storeList = resource.data;
|
||||
updateStoreSpinnerSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the customer spinner with the current list and sets the selection if needed.
|
||||
*/
|
||||
@@ -298,7 +260,7 @@ public class PetDetailFragment extends Fragment {
|
||||
SpinnerUtils.populateSpinner(
|
||||
requireContext(),
|
||||
binding.spinnerCustomer,
|
||||
customerList,
|
||||
viewModel.getCustomerList().getValue(),
|
||||
DropdownDTO::getLabel,
|
||||
"No Owner",
|
||||
selectedCustomerId,
|
||||
@@ -313,7 +275,7 @@ public class PetDetailFragment extends Fragment {
|
||||
SpinnerUtils.populateSpinner(
|
||||
requireContext(),
|
||||
binding.spinnerStore,
|
||||
storeList,
|
||||
viewModel.getStoreList().getValue(),
|
||||
DropdownDTO::getLabel,
|
||||
"None",
|
||||
selectedStoreId,
|
||||
@@ -333,11 +295,9 @@ public class PetDetailFragment extends Fragment {
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
String status = parent.getItemAtPosition(position).toString();
|
||||
|
||||
// Clear any existing error icons when status changes
|
||||
clearSpinnerError(binding.spinnerCustomer);
|
||||
clearSpinnerError(binding.spinnerStore);
|
||||
|
||||
//Disable the customer spinner if the status is "Available"
|
||||
if ("Available".equalsIgnoreCase(status)) {
|
||||
binding.spinnerCustomer.setSelection(0);
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerCustomer);
|
||||
@@ -345,7 +305,6 @@ public class PetDetailFragment extends Fragment {
|
||||
UIUtils.setViewsEnabled(true, binding.spinnerCustomer);
|
||||
}
|
||||
|
||||
//Disable the store spinner if the status is "Owned"
|
||||
if ("Owned".equalsIgnoreCase(status)) {
|
||||
binding.spinnerStore.setSelection(0);
|
||||
UIUtils.setViewsEnabled(false, binding.spinnerStore);
|
||||
@@ -360,9 +319,6 @@ public class PetDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears error messages from a Spinner's selected view.
|
||||
*/
|
||||
private void clearSpinnerError(Spinner spinner) {
|
||||
View selectedView = spinner.getSelectedView();
|
||||
if (selectedView instanceof TextView) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import com.example.petstoremobile.api.*;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.databinding.FragmentProductDetailBinding;
|
||||
import com.example.petstoremobile.dtos.*;
|
||||
import com.example.petstoremobile.viewmodels.ProductViewModel;
|
||||
import com.example.petstoremobile.viewmodels.ProductDetailViewModel;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.FileUtils;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
@@ -31,7 +31,6 @@ import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
@@ -46,29 +45,22 @@ import okhttp3.RequestBody;
|
||||
public class ProductDetailFragment extends Fragment {
|
||||
|
||||
private FragmentProductDetailBinding binding;
|
||||
private ProductDetailViewModel viewModel;
|
||||
private ImagePickerHelper imagePickerHelper;
|
||||
|
||||
private long prodId = -1;
|
||||
private boolean isEditing = false;
|
||||
private long preselectedCategoryId = -1;
|
||||
private boolean hasImage = false;
|
||||
private boolean isImageChanged = false;
|
||||
private boolean isImageRemoved = false;
|
||||
|
||||
private List<CategoryDTO> categoryList = new ArrayList<>();
|
||||
private Uri photoUri;
|
||||
private ProductViewModel viewModel;
|
||||
private ImagePickerHelper imagePickerHelper;
|
||||
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
/**
|
||||
* Initializes activity launchers and the ImagePickerHelper.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ProductViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(ProductDetailViewModel.class);
|
||||
|
||||
imagePickerHelper = new ImagePickerHelper(this, "product_photo.jpg", new ImagePickerHelper.ImagePickerListener() {
|
||||
@Override
|
||||
@@ -95,9 +87,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -105,14 +94,11 @@ public class ProductDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components and listeners after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
loadCategories();
|
||||
observeViewModel();
|
||||
handleArguments();
|
||||
|
||||
binding.btnProductBack.setOnClickListener(v -> navigateBack());
|
||||
@@ -121,34 +107,33 @@ public class ProductDetailFragment extends Fragment {
|
||||
binding.ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage));
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getCategoryList().observe(getViewLifecycleOwner(), list -> updateCategorySpinner());
|
||||
|
||||
viewModel.loadCategories().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setCategoryList(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateCategorySpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, viewModel.getCategoryList().getValue(),
|
||||
CategoryDTO::getCategoryName, "-- Select Category --",
|
||||
preselectedCategoryId, CategoryDTO::getCategoryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all product categories for the selection spinner.
|
||||
*/
|
||||
private void loadCategories() {
|
||||
viewModel.getAllCategories(0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
categoryList = resource.data.getContent();
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, categoryList,
|
||||
CategoryDTO::getCategoryName, "-- Select Category --",
|
||||
preselectedCategoryId, CategoryDTO::getCategoryId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the fragment was opened with existing product data for editing.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("prodId")) {
|
||||
isEditing = true;
|
||||
prodId = a.getLong("prodId");
|
||||
long prodId = a.getLong("prodId");
|
||||
viewModel.setProdId(prodId);
|
||||
binding.tvProductMode.setText("Edit Product");
|
||||
binding.tvProductId.setText("ID: " + prodId);
|
||||
binding.tvProductId.setVisibility(View.VISIBLE);
|
||||
@@ -156,6 +141,7 @@ public class ProductDetailFragment extends Fragment {
|
||||
loadProductData();
|
||||
loadProductImage();
|
||||
} else {
|
||||
viewModel.setProdId(-1);
|
||||
binding.tvProductMode.setText("Add Product");
|
||||
binding.btnDeleteProduct.setVisibility(View.GONE);
|
||||
binding.tvProductId.setVisibility(View.GONE);
|
||||
@@ -163,11 +149,8 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product data from the backend.
|
||||
*/
|
||||
private void loadProductData() {
|
||||
viewModel.getProductById(prodId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ProductDTO p = resource.data;
|
||||
@@ -175,24 +158,15 @@ public class ProductDetailFragment extends Fragment {
|
||||
binding.etProductDesc.setText(p.getProdDesc());
|
||||
binding.etProductPrice.setText(p.getProdPrice() != null ? p.getProdPrice().toString() : "");
|
||||
preselectedCategoryId = p.getCategoryId() != null ? p.getCategoryId() : -1;
|
||||
|
||||
// Refresh spinner selection once data is loaded
|
||||
if (!categoryList.isEmpty()) {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, categoryList,
|
||||
CategoryDTO::getCategoryName, "-- Select Category --",
|
||||
preselectedCategoryId, CategoryDTO::getCategoryId);
|
||||
}
|
||||
updateCategorySpinner();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load product: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product image from the backend.
|
||||
*/
|
||||
private void loadProductImage() {
|
||||
String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, prodId);
|
||||
String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, viewModel.getProdId());
|
||||
String token = tokenManager.getToken();
|
||||
|
||||
GlideUtils.loadImageWithToken(requireContext(), binding.ivProductImage, imageUrl, token, R.drawable.placeholder2, new GlideUtils.ImageLoadListener() {
|
||||
@@ -208,12 +182,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs image related actions (upload/delete) after product details are saved.
|
||||
*/
|
||||
private void performPendingImageActions(String successMsg) {
|
||||
if (isImageRemoved) {
|
||||
viewModel.deleteProductImage(prodId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteProductImage().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
@@ -231,9 +202,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the selected image file to the server.
|
||||
*/
|
||||
private void uploadProductImageAndNavigate(Uri uri, String successMsg) {
|
||||
File file = FileUtils.getFileFromUri(requireContext(), uri);
|
||||
if (file == null) {
|
||||
@@ -245,7 +213,7 @@ public class ProductDetailFragment extends Fragment {
|
||||
RequestBody requestFile = RequestBody.create(file, MediaType.parse(requireContext().getContentResolver().getType(uri)));
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
|
||||
|
||||
viewModel.uploadProductImage(prodId, body).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.uploadProductImage(body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
@@ -257,9 +225,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input fields and saves product information to the backend.
|
||||
*/
|
||||
private void saveProduct() {
|
||||
if (!InputValidator.isNotEmpty(binding.etProductName, "Product Name")) return;
|
||||
|
||||
@@ -276,39 +241,26 @@ public class ProductDetailFragment extends Fragment {
|
||||
String desc = binding.etProductDesc.getText().toString().trim();
|
||||
BigDecimal price = new BigDecimal(binding.etProductPrice.getText().toString().trim());
|
||||
|
||||
CategoryDTO category = categoryList.get(binding.spinnerProductCategory.getSelectedItemPosition() - 1);
|
||||
CategoryDTO category = viewModel.getCategoryList().getValue().get(binding.spinnerProductCategory.getSelectedItemPosition() - 1);
|
||||
ProductDTO dto = new ProductDTO(name, category.getCategoryId(), desc, price);
|
||||
|
||||
if (isEditing) {
|
||||
viewModel.updateProduct(prodId, dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
performPendingImageActions("Updated");
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
viewModel.saveProduct(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (resource.data != null) {
|
||||
viewModel.setProdId(resource.data.getProdId());
|
||||
}
|
||||
performPendingImageActions(viewModel.isEditing() ? "Updated" : "Saved");
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
viewModel.createProduct(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
prodId = resource.data.getProdId();
|
||||
performPendingImageActions("Saved");
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Error saving: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog before deleting the product.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product", () ->
|
||||
viewModel.deleteProduct(prodId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS) {
|
||||
navigateBack();
|
||||
} else if (resource != null && resource.status == Resource.Status.ERROR) {
|
||||
@@ -317,9 +269,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous fragment.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
@@ -15,9 +15,7 @@ import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.viewmodels.ProductSupplierViewModel;
|
||||
import com.example.petstoremobile.viewmodels.ProductViewModel;
|
||||
import com.example.petstoremobile.viewmodels.SupplierViewModel;
|
||||
import com.example.petstoremobile.viewmodels.ProductSupplierDetailViewModel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
@@ -31,26 +29,15 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class ProductSupplierDetailFragment extends Fragment {
|
||||
|
||||
private FragmentProductSupplierDetailBinding binding;
|
||||
private ProductSupplierDetailViewModel viewModel;
|
||||
|
||||
private boolean isEditing = false;
|
||||
private long editProductId = -1;
|
||||
private long editSupplierId = -1;
|
||||
private long preselectedProductId = -1;
|
||||
private long preselectedSupplierId = -1;
|
||||
|
||||
private List<ProductDTO> productList = new ArrayList<>();
|
||||
private List<SupplierDTO> supplierList = new ArrayList<>();
|
||||
|
||||
private ProductSupplierViewModel psViewModel;
|
||||
private ProductViewModel productViewModel;
|
||||
private SupplierViewModel supplierViewModel;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
psViewModel = new ViewModelProvider(this).get(ProductSupplierViewModel.class);
|
||||
productViewModel = new ViewModelProvider(this).get(ProductViewModel.class);
|
||||
supplierViewModel = new ViewModelProvider(this).get(SupplierViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(ProductSupplierDetailViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,6 +50,7 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
observeViewModel();
|
||||
loadSpinnersData();
|
||||
handleArguments();
|
||||
|
||||
@@ -71,81 +59,59 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
binding.btnDeletePS.setOnClickListener(v -> confirmDelete());
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
|
||||
viewModel.getSupplierList().observe(getViewLifecycleOwner(), list -> refreshSupplierSpinner());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches products and suppliers to populate the spinners.
|
||||
*/
|
||||
private void loadSpinnersData() {
|
||||
loadProducts();
|
||||
loadSuppliers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of products from the API.
|
||||
*/
|
||||
private void loadProducts() {
|
||||
productViewModel.getAllProducts(null, null, 0, 200, "prodName").observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
productList = resource.data.getContent();
|
||||
refreshProductSpinner();
|
||||
viewModel.setProductList(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
viewModel.loadSuppliers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setSupplierList(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshProductSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSProduct, productList,
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSProduct, viewModel.getProductList().getValue(),
|
||||
ProductDTO::getProdName, "-- Select Product --",
|
||||
preselectedProductId, ProductDTO::getProdId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of suppliers from the API.
|
||||
*/
|
||||
private void loadSuppliers() {
|
||||
supplierViewModel.getAllSuppliers(0, 200, null, "supCompany").observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
supplierList = resource.data.getContent();
|
||||
refreshSupplierSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshSupplierSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSSupplier, supplierList,
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSSupplier, viewModel.getSupplierList().getValue(),
|
||||
SupplierDTO::getSupCompany, "-- Select Supplier --",
|
||||
preselectedSupplierId, SupplierDTO::getSupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments to determine if the fragment is in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("productId") && a.containsKey("supplierId")) {
|
||||
isEditing = true;
|
||||
editProductId = a.getLong("productId");
|
||||
editSupplierId = a.getLong("supplierId");
|
||||
preselectedProductId = editProductId;
|
||||
preselectedSupplierId = editSupplierId;
|
||||
long productId = a.getLong("productId");
|
||||
long supplierId = a.getLong("supplierId");
|
||||
viewModel.setEditMode(productId, supplierId);
|
||||
preselectedProductId = productId;
|
||||
preselectedSupplierId = supplierId;
|
||||
|
||||
binding.tvPSMode.setText("Edit Product Supplier");
|
||||
binding.btnDeletePS.setVisibility(View.VISIBLE);
|
||||
|
||||
} else {
|
||||
binding.tvPSMode.setText("Add Product Supplier");
|
||||
binding.btnDeletePS.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates input and saves the product-supplier to the backend.
|
||||
*/
|
||||
private void save() {
|
||||
if (binding.spinnerPSProduct.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a product", Toast.LENGTH_SHORT).show(); return;
|
||||
@@ -159,40 +125,25 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
|
||||
ProductDTO product = productList.get(binding.spinnerPSProduct.getSelectedItemPosition() - 1);
|
||||
SupplierDTO supplier = supplierList.get(binding.spinnerPSSupplier.getSelectedItemPosition() - 1);
|
||||
ProductDTO product = viewModel.getProductList().getValue().get(binding.spinnerPSProduct.getSelectedItemPosition() - 1);
|
||||
SupplierDTO supplier = viewModel.getSupplierList().getValue().get(binding.spinnerPSSupplier.getSelectedItemPosition() - 1);
|
||||
BigDecimal cost = new BigDecimal(binding.etPSCost.getText().toString().trim());
|
||||
|
||||
ProductSupplierDTO dto = new ProductSupplierDTO(
|
||||
product.getProdId(), supplier.getSupId(), cost);
|
||||
ProductSupplierDTO dto = new ProductSupplierDTO(product.getProdId(), supplier.getSupId(), cost);
|
||||
|
||||
if (isEditing) {
|
||||
psViewModel.updateProductSupplier(editProductId, editSupplierId, dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Updated", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
psViewModel.createProductSupplier(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Saved", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
viewModel.saveProductSupplier(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Updated" : "Saved", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a confirmation dialog before deleting a product-supplier relationship.
|
||||
*/
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product Supplier", () ->
|
||||
psViewModel.deleteProductSupplier(editProductId, editSupplierId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteProductSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Deleted", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
@@ -202,9 +153,6 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.databinding.FragmentPurchaseOrderDetailBinding;
|
||||
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.viewmodels.PurchaseOrderViewModel;
|
||||
import com.example.petstoremobile.viewmodels.PurchaseOrderDetailViewModel;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@@ -25,13 +25,13 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class PurchaseOrderDetailFragment extends Fragment {
|
||||
|
||||
private FragmentPurchaseOrderDetailBinding binding;
|
||||
private PurchaseOrderViewModel viewModel;
|
||||
private PurchaseOrderDetailViewModel viewModel;
|
||||
private long purchaseOrderId;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PurchaseOrderViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(PurchaseOrderDetailViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ public class PurchaseOrderDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void loadPurchaseOrderData() {
|
||||
viewModel.getPurchaseOrderById(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadPurchaseOrder(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PurchaseOrderDTO po = resource.data;
|
||||
|
||||
@@ -12,7 +12,8 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentRefundBinding;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.viewmodels.SaleViewModel;
|
||||
import com.example.petstoremobile.viewmodels.RefundViewModel;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
@@ -22,53 +23,23 @@ import java.util.*;
|
||||
public class RefundFragment extends Fragment {
|
||||
|
||||
private FragmentRefundBinding binding;
|
||||
private SaleViewModel saleViewModel;
|
||||
private SaleDTO currentSale;
|
||||
private List<SaleDTO> allSales = new ArrayList<>();
|
||||
|
||||
// Items available to refund (after accounting for previous refunds)
|
||||
private List<RefundItem> availableItems = new ArrayList<>();
|
||||
// Items user has added to refund cart
|
||||
private List<RefundItem> refundCart = new ArrayList<>();
|
||||
private RefundViewModel viewModel;
|
||||
|
||||
private final String[] PAYMENT_METHODS = {"Cash", "Card"};
|
||||
|
||||
// Inner class to track refund items
|
||||
static class RefundItem {
|
||||
long prodId;
|
||||
String productName;
|
||||
int quantity;
|
||||
BigDecimal unitPrice;
|
||||
|
||||
RefundItem(long prodId, String productName, int quantity, BigDecimal unitPrice) {
|
||||
this.prodId = prodId;
|
||||
this.productName = productName;
|
||||
this.quantity = quantity;
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
|
||||
BigDecimal getTotal() {
|
||||
return unitPrice != null
|
||||
? unitPrice.multiply(BigDecimal.valueOf(quantity))
|
||||
: BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentRefundBinding.inflate(inflater, container, false);
|
||||
saleViewModel = new ViewModelProvider(this).get(SaleViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(RefundViewModel.class);
|
||||
|
||||
setupSpinner();
|
||||
observeViewModel();
|
||||
loadAllSales();
|
||||
|
||||
// Pre-fill sale ID if passed from SaleFragment
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("saleId")) {
|
||||
long saleId = args.getLong("saleId");
|
||||
binding.etRefundSaleId.setText(String.valueOf(saleId));
|
||||
// Auto-load after sales are fetched
|
||||
binding.etRefundSaleId.setText(String.valueOf(args.getLong("saleId")));
|
||||
}
|
||||
|
||||
binding.btnLoadSale.setOnClickListener(v -> loadSale());
|
||||
@@ -83,27 +54,25 @@ public class RefundFragment extends Fragment {
|
||||
android.R.layout.simple_spinner_item, PAYMENT_METHODS));
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getAvailableItems().observe(getViewLifecycleOwner(), items -> renderOriginalItems());
|
||||
viewModel.getRefundCart().observe(getViewLifecycleOwner(), cart -> {
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
renderOriginalItems(); // Re-render to reflect quantities in cart
|
||||
});
|
||||
}
|
||||
|
||||
private void loadAllSales() {
|
||||
saleViewModel.getAllSales(0, 1000, null, null, null, "saleDate,desc")
|
||||
.observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
switch (resource.status) {
|
||||
case SUCCESS:
|
||||
if (resource.data != null) {
|
||||
allSales = resource.data.getContent();
|
||||
// Auto-load if saleId was pre-filled
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("saleId")) {
|
||||
loadSale();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
Log.e("Refund", "Failed to load sales: " + resource.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
viewModel.loadAllSales().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setAllSales(resource.data.getContent());
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("saleId")) {
|
||||
loadSale();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSale() {
|
||||
@@ -120,11 +89,12 @@ public class RefundFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find sale in loaded list
|
||||
SaleDTO found = null;
|
||||
for (SaleDTO s : allSales) {
|
||||
if (s.getSaleId() != null && s.getSaleId() == saleId) {
|
||||
found = s; break;
|
||||
if (viewModel.getAllSalesList() != null) {
|
||||
for (SaleDTO s : viewModel.getAllSalesList()) {
|
||||
if (s.getSaleId() != null && s.getSaleId() == saleId) {
|
||||
found = s; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,9 +109,9 @@ public class RefundFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSale = found;
|
||||
viewModel.setCurrentSale(found);
|
||||
SaleDTO currentSale = viewModel.getCurrentSale();
|
||||
|
||||
// Show sale info
|
||||
binding.tvSaleInfo.setVisibility(View.VISIBLE);
|
||||
binding.tvSaleInfo.setText("Sale #" + currentSale.getSaleId()
|
||||
+ " | " + (currentSale.getSaleDate() != null
|
||||
@@ -151,7 +121,6 @@ public class RefundFragment extends Fragment {
|
||||
+ " | Total: $" + currentSale.getTotalAmount()
|
||||
+ " | Payment: " + currentSale.getPaymentMethod());
|
||||
|
||||
// Pre-select payment method
|
||||
if (currentSale.getPaymentMethod() != null) {
|
||||
for (int i = 0; i < PAYMENT_METHODS.length; i++) {
|
||||
if (PAYMENT_METHODS[i].equalsIgnoreCase(currentSale.getPaymentMethod())) {
|
||||
@@ -160,85 +129,40 @@ public class RefundFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
// Build refundable items accounting for previous refunds
|
||||
buildRefundableItems();
|
||||
|
||||
if (availableItems.isEmpty()) {
|
||||
Toast.makeText(getContext(),
|
||||
"This sale has no remaining refundable items", Toast.LENGTH_LONG).show();
|
||||
if (viewModel.getAvailableItems().getValue() == null || viewModel.getAvailableItems().getValue().isEmpty()) {
|
||||
Toast.makeText(getContext(), "This sale has no remaining refundable items", Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset refund cart
|
||||
refundCart.clear();
|
||||
|
||||
// Show cards
|
||||
binding.cardOriginalItems.setVisibility(View.VISIBLE);
|
||||
binding.cardRefundItems.setVisibility(View.VISIBLE);
|
||||
binding.cardPayment.setVisibility(View.VISIBLE);
|
||||
binding.btnProcessRefund.setVisibility(View.VISIBLE);
|
||||
|
||||
renderOriginalItems();
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
}
|
||||
|
||||
private void buildRefundableItems() {
|
||||
availableItems.clear();
|
||||
if (currentSale.getItems() == null) return;
|
||||
|
||||
// Find all previous refunds for this sale
|
||||
Map<Long, Integer> alreadyRefunded = new HashMap<>();
|
||||
for (SaleDTO s : allSales) {
|
||||
if (Boolean.TRUE.equals(s.getIsRefund())
|
||||
&& currentSale.getSaleId().equals(s.getOriginalSaleId())
|
||||
&& s.getItems() != null) {
|
||||
for (SaleDTO.SaleItemDTO item : s.getItems()) {
|
||||
if (item.getProdId() != null && item.getQuantity() != null) {
|
||||
alreadyRefunded.merge(item.getProdId(),
|
||||
Math.abs(item.getQuantity()), Integer::sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build available items
|
||||
for (SaleDTO.SaleItemDTO item : currentSale.getItems()) {
|
||||
if (item.getProdId() == null || item.getQuantity() == null) continue;
|
||||
int refunded = alreadyRefunded.getOrDefault(item.getProdId(), 0);
|
||||
int remaining = item.getQuantity() - refunded;
|
||||
if (remaining > 0) {
|
||||
availableItems.add(new RefundItem(
|
||||
item.getProdId(),
|
||||
item.getProductName() != null ? item.getProductName() : "Unknown",
|
||||
remaining,
|
||||
item.getUnitPrice()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderOriginalItems() {
|
||||
binding.llOriginalItems.removeAllViews();
|
||||
List<RefundViewModel.RefundItem> available = viewModel.getAvailableItems().getValue();
|
||||
if (available == null) return;
|
||||
|
||||
// Header
|
||||
addTableHeader(binding.llOriginalItems);
|
||||
|
||||
for (RefundItem item : availableItems) {
|
||||
// Calculate pending in cart
|
||||
int pendingQty = 0;
|
||||
for (RefundItem r : refundCart) {
|
||||
if (r.prodId == item.prodId) { pendingQty = r.quantity; break; }
|
||||
for (RefundViewModel.RefundItem item : available) {
|
||||
int inCart = 0;
|
||||
if (viewModel.getRefundCart().getValue() != null) {
|
||||
for (RefundViewModel.RefundItem r : viewModel.getRefundCart().getValue()) {
|
||||
if (r.prodId == item.prodId) { inCart = r.quantity; break; }
|
||||
}
|
||||
}
|
||||
int displayQty = item.quantity - pendingQty;
|
||||
int displayQty = item.quantity - inCart;
|
||||
if (displayQty <= 0) continue;
|
||||
|
||||
LinearLayout row = buildItemRow(
|
||||
item.productName,
|
||||
displayQty,
|
||||
item.unitPrice,
|
||||
true, // show add button
|
||||
() -> showQuantityDialog(item)
|
||||
true,
|
||||
() -> showQuantityDialog(item, displayQty)
|
||||
);
|
||||
binding.llOriginalItems.addView(row);
|
||||
}
|
||||
@@ -246,8 +170,9 @@ public class RefundFragment extends Fragment {
|
||||
|
||||
private void renderRefundCart() {
|
||||
binding.llRefundItems.removeAllViews();
|
||||
List<RefundViewModel.RefundItem> cart = viewModel.getRefundCart().getValue();
|
||||
|
||||
if (refundCart.isEmpty()) {
|
||||
if (cart == null || cart.isEmpty()) {
|
||||
TextView empty = new TextView(getContext());
|
||||
empty.setText("No items added to refund yet");
|
||||
empty.setTextColor(0xFF888780);
|
||||
@@ -258,18 +183,13 @@ public class RefundFragment extends Fragment {
|
||||
|
||||
addTableHeader(binding.llRefundItems);
|
||||
|
||||
for (RefundItem item : refundCart) {
|
||||
for (RefundViewModel.RefundItem item : cart) {
|
||||
LinearLayout row = buildItemRow(
|
||||
item.productName,
|
||||
item.quantity,
|
||||
item.unitPrice,
|
||||
false, // show remove button
|
||||
() -> {
|
||||
refundCart.remove(item);
|
||||
renderOriginalItems();
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
}
|
||||
false,
|
||||
() -> viewModel.removeFromCart(item)
|
||||
);
|
||||
binding.llRefundItems.addView(row);
|
||||
}
|
||||
@@ -342,24 +262,10 @@ public class RefundFragment extends Fragment {
|
||||
return row;
|
||||
}
|
||||
|
||||
private void showQuantityDialog(RefundItem item) {
|
||||
// Calculate how many are already in cart
|
||||
int inCart = 0;
|
||||
for (RefundItem r : refundCart) {
|
||||
if (r.prodId == item.prodId) { inCart = r.quantity; break; }
|
||||
}
|
||||
int available = item.quantity - inCart;
|
||||
if (available <= 0) {
|
||||
Toast.makeText(getContext(), "All units already added to refund",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Build dialog
|
||||
private void showQuantityDialog(RefundViewModel.RefundItem item, int available) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
||||
builder.setTitle("Refund Quantity");
|
||||
builder.setMessage("Product: " + item.productName
|
||||
+ "\nAvailable: " + available);
|
||||
builder.setMessage("Product: " + item.productName + "\nAvailable: " + available);
|
||||
|
||||
EditText input = new EditText(getContext());
|
||||
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
|
||||
@@ -377,36 +283,15 @@ public class RefundFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
if (qty <= 0) {
|
||||
Toast.makeText(getContext(), "Quantity must be at least 1",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getContext(), "Quantity must be at least 1", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (qty > available) {
|
||||
Toast.makeText(getContext(), "Cannot exceed " + available,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getContext(), "Cannot exceed " + available, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Add or merge into cart
|
||||
boolean merged = false;
|
||||
for (int i = 0; i < refundCart.size(); i++) {
|
||||
if (refundCart.get(i).prodId == item.prodId) {
|
||||
RefundItem existing = refundCart.get(i);
|
||||
refundCart.set(i, new RefundItem(existing.prodId,
|
||||
existing.productName,
|
||||
existing.quantity + qty,
|
||||
existing.unitPrice));
|
||||
merged = true; break;
|
||||
}
|
||||
}
|
||||
if (!merged) {
|
||||
refundCart.add(new RefundItem(item.prodId, item.productName,
|
||||
qty, item.unitPrice));
|
||||
}
|
||||
|
||||
renderOriginalItems();
|
||||
renderRefundCart();
|
||||
updateRefundTotal();
|
||||
viewModel.addToCart(item, qty);
|
||||
});
|
||||
|
||||
builder.setNegativeButton("Cancel", null);
|
||||
@@ -415,31 +300,31 @@ public class RefundFragment extends Fragment {
|
||||
|
||||
private void updateRefundTotal() {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (RefundItem item : refundCart) total = total.add(item.getTotal());
|
||||
List<RefundViewModel.RefundItem> cart = viewModel.getRefundCart().getValue();
|
||||
if (cart != null) {
|
||||
for (RefundViewModel.RefundItem item : cart) total = total.add(item.getTotal());
|
||||
}
|
||||
binding.tvRefundTotal.setText("Refund Total: $" + total.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
private void processRefund() {
|
||||
if (currentSale == null) {
|
||||
if (viewModel.getCurrentSale() == null) {
|
||||
Toast.makeText(getContext(), "Load a sale first", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (refundCart.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Add at least one item to refund",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
if (viewModel.getRefundCart().getValue() == null || viewModel.getRefundCart().getValue().isEmpty()) {
|
||||
Toast.makeText(getContext(), "Add at least one item to refund", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String payment = PAYMENT_METHODS[binding.spinnerRefundPayment.getSelectedItemPosition()];
|
||||
|
||||
// Confirm dialog
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (RefundItem item : refundCart) total = total.add(item.getTotal());
|
||||
for (RefundViewModel.RefundItem item : viewModel.getRefundCart().getValue()) total = total.add(item.getTotal());
|
||||
final BigDecimal finalTotal = total;
|
||||
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Confirm Refund")
|
||||
.setMessage("Process refund for Sale #" + currentSale.getSaleId()
|
||||
.setMessage("Process refund for Sale #" + viewModel.getCurrentSale().getSaleId()
|
||||
+ "?\nRefund amount: $" + finalTotal.setScale(2, RoundingMode.HALF_UP))
|
||||
.setPositiveButton("Yes", (d, w) -> submitRefund(payment))
|
||||
.setNegativeButton("No", null)
|
||||
@@ -447,41 +332,13 @@ public class RefundFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void submitRefund(String payment) {
|
||||
// Build sale items list
|
||||
List<SaleDTO.SaleItemDTO> items = new ArrayList<>();
|
||||
for (RefundItem item : refundCart) {
|
||||
// Backend expects negative quantity for refunds
|
||||
items.add(new SaleDTO.SaleItemDTO(item.prodId, -item.quantity));
|
||||
}
|
||||
|
||||
SaleDTO dto = new SaleDTO(
|
||||
currentSale.getStoreId(),
|
||||
payment,
|
||||
items,
|
||||
true, // isRefund = true
|
||||
currentSale.getSaleId(), // originalSaleId
|
||||
null // no customer needed
|
||||
);
|
||||
|
||||
Log.d("REFUND", "Submitting refund for saleId=" + currentSale.getSaleId()
|
||||
+ " items=" + items.size());
|
||||
|
||||
saleViewModel.createSale(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.submitRefund(payment).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
switch (resource.status) {
|
||||
case SUCCESS:
|
||||
if (resource.data != null) {
|
||||
Toast.makeText(getContext(),
|
||||
"Refund #" + resource.data.getSaleId() + " processed successfully!",
|
||||
Toast.LENGTH_LONG).show();
|
||||
navigateBack();
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
Log.e("REFUND", "Error: " + resource.message);
|
||||
Toast.makeText(getContext(), "Error: " + resource.message,
|
||||
Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Refund processed successfully!", Toast.LENGTH_LONG).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentSaleDetailBinding;
|
||||
import com.example.petstoremobile.dtos.*;
|
||||
import com.example.petstoremobile.viewmodels.*;
|
||||
import com.example.petstoremobile.viewmodels.SaleDetailViewModel;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
@@ -24,18 +24,7 @@ import java.util.*;
|
||||
public class SaleDetailFragment extends Fragment {
|
||||
|
||||
private FragmentSaleDetailBinding binding;
|
||||
private SaleViewModel saleViewModel;
|
||||
private StoreViewModel storeViewModel;
|
||||
private CustomerViewModel customerViewModel;
|
||||
private ProductViewModel productViewModel;
|
||||
|
||||
private boolean viewOnly = false;
|
||||
private long saleId = -1;
|
||||
|
||||
private List<DropdownDTO> storeList = new ArrayList<>();
|
||||
private List<DropdownDTO> customerList = new ArrayList<>();
|
||||
private List<ProductDTO> productList = new ArrayList<>();
|
||||
private List<SaleDTO.SaleItemDTO> cartItems = new ArrayList<>();
|
||||
private SaleDetailViewModel viewModel;
|
||||
|
||||
private final String[] PAYMENT_METHODS = { "Cash", "Card"};
|
||||
|
||||
@@ -43,17 +32,14 @@ public class SaleDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentSaleDetailBinding.inflate(inflater, container, false);
|
||||
|
||||
saleViewModel = new ViewModelProvider(this).get(SaleViewModel.class);
|
||||
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
|
||||
customerViewModel = new ViewModelProvider(this).get(CustomerViewModel.class);
|
||||
productViewModel = new ViewModelProvider(this).get(ProductViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(SaleDetailViewModel.class);
|
||||
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPaymentMethod, PAYMENT_METHODS);
|
||||
|
||||
observeViewModel();
|
||||
handleArguments();
|
||||
|
||||
if (!viewOnly) {
|
||||
if (!viewModel.isViewOnly()) {
|
||||
loadData();
|
||||
setupAddItem();
|
||||
}
|
||||
@@ -65,20 +51,42 @@ public class SaleDetailFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerSaleStore, list,
|
||||
DropdownDTO::getLabel, "-- Select Store --", -1L, DropdownDTO::getId);
|
||||
});
|
||||
|
||||
viewModel.getCustomerList().observe(getViewLifecycleOwner(), list -> {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerSaleCustomer, list,
|
||||
DropdownDTO::getLabel, "-- No Customer --", -1L, DropdownDTO::getId);
|
||||
});
|
||||
|
||||
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerSaleProduct, list,
|
||||
ProductDTO::getProdName, "Select Product", -1L, ProductDTO::getProdId);
|
||||
});
|
||||
|
||||
viewModel.getCartItems().observe(getViewLifecycleOwner(), items -> {
|
||||
renderCartItems();
|
||||
updateTotal();
|
||||
});
|
||||
}
|
||||
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("saleId")) {
|
||||
saleId = a.getLong("saleId");
|
||||
viewOnly = a.getBoolean("viewOnly", false);
|
||||
long saleId = a.getLong("saleId");
|
||||
boolean viewOnly = a.getBoolean("viewOnly", false);
|
||||
viewModel.setSaleId(saleId, viewOnly);
|
||||
|
||||
binding.tvSaleMode.setText("Sale #" + saleId);
|
||||
binding.tvSaleDetailId.setText("ID: " + saleId);
|
||||
|
||||
// Show refund button for existing non-refund sales
|
||||
if (!a.getBoolean("isRefund", false)) {
|
||||
binding.btnRefundSale.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// Hide save and input controls for view only
|
||||
if (viewOnly) {
|
||||
binding.btnSaveSale.setVisibility(View.GONE);
|
||||
UIUtils.setViewsEnabled(false,
|
||||
@@ -89,9 +97,9 @@ public class SaleDetailFragment extends Fragment {
|
||||
binding.llExtraInfo.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// Load sale details
|
||||
loadSaleDetails();
|
||||
} else {
|
||||
viewModel.setSaleId(-1, false);
|
||||
binding.tvSaleMode.setText("New Sale");
|
||||
binding.tvSaleDetailId.setVisibility(View.GONE);
|
||||
binding.btnRefundSale.setVisibility(View.GONE);
|
||||
@@ -100,82 +108,47 @@ public class SaleDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void loadData() {
|
||||
loadStores();
|
||||
loadCustomers();
|
||||
loadProducts();
|
||||
}
|
||||
|
||||
private void loadStores() {
|
||||
storeViewModel.getStoreDropdowns().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
storeList = resource.data;
|
||||
if (binding != null) {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerSaleStore, storeList,
|
||||
DropdownDTO::getLabel, "-- Select Store --", -1L, DropdownDTO::getId);
|
||||
}
|
||||
}
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) viewModel.setStoreList(resource.data);
|
||||
});
|
||||
}
|
||||
|
||||
private void loadCustomers() {
|
||||
customerViewModel.getCustomerDropdowns().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
customerList = resource.data;
|
||||
if (binding != null) {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerSaleCustomer, customerList,
|
||||
DropdownDTO::getLabel, "-- No Customer --", -1L, DropdownDTO::getId);
|
||||
}
|
||||
}
|
||||
viewModel.loadCustomers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) viewModel.setCustomerList(resource.data);
|
||||
});
|
||||
}
|
||||
|
||||
private void loadProducts() {
|
||||
productViewModel.getAllProducts(null, null, 0, 200, null).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
productList = resource.data.getContent();
|
||||
if (binding != null) {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerSaleProduct, productList,
|
||||
ProductDTO::getProdName, "Select Product", -1L, ProductDTO::getProdId);
|
||||
}
|
||||
}
|
||||
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) viewModel.setProductList(resource.data.getContent());
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSaleDetails() {
|
||||
saleViewModel.getSaleById(saleId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadSaleDetails().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
SaleDTO sale = resource.data;
|
||||
if (binding != null) {
|
||||
binding.tvSaleDetailTotal.setText("Total: $" + sale.getTotalAmount());
|
||||
binding.tvSaleSubtotal.setText("$" + (sale.getSubtotalAmount() != null ? sale.getSubtotalAmount() : sale.getTotalAmount()));
|
||||
|
||||
if (sale.getCouponDiscountAmount() != null && sale.getCouponDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
binding.llCouponDiscount.setVisibility(View.VISIBLE);
|
||||
binding.tvSaleCouponDiscount.setText("-$" + sale.getCouponDiscountAmount());
|
||||
} else {
|
||||
binding.llCouponDiscount.setVisibility(View.GONE);
|
||||
}
|
||||
binding.tvSaleDetailTotal.setText("Total: $" + sale.getTotalAmount());
|
||||
binding.tvSaleSubtotal.setText("$" + (sale.getSubtotalAmount() != null ? sale.getSubtotalAmount() : sale.getTotalAmount()));
|
||||
|
||||
if (sale.getCouponDiscountAmount() != null && sale.getCouponDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
binding.llCouponDiscount.setVisibility(View.VISIBLE);
|
||||
binding.tvSaleCouponDiscount.setText("-$" + sale.getCouponDiscountAmount());
|
||||
} else {
|
||||
binding.llCouponDiscount.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (sale.getEmployeeDiscountAmount() != null && sale.getEmployeeDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
binding.llEmployeeDiscount.setVisibility(View.VISIBLE);
|
||||
binding.tvSaleEmployeeDiscount.setText("-$" + sale.getEmployeeDiscountAmount());
|
||||
} else {
|
||||
binding.llEmployeeDiscount.setVisibility(View.GONE);
|
||||
}
|
||||
if (sale.getEmployeeDiscountAmount() != null && sale.getEmployeeDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||
binding.llEmployeeDiscount.setVisibility(View.VISIBLE);
|
||||
binding.tvSaleEmployeeDiscount.setText("-$" + sale.getEmployeeDiscountAmount());
|
||||
} else {
|
||||
binding.llEmployeeDiscount.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
binding.tvSaleChannel.setText(sale.getChannel() != null ? sale.getChannel() : "—");
|
||||
binding.tvSalePoints.setText(String.valueOf(sale.getPointsEarned() != null ? sale.getPointsEarned() : 0));
|
||||
binding.tvSaleChannel.setText(sale.getChannel() != null ? sale.getChannel() : "—");
|
||||
binding.tvSalePoints.setText(String.valueOf(sale.getPointsEarned() != null ? sale.getPointsEarned() : 0));
|
||||
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerPaymentMethod, sale.getPaymentMethod());
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerPaymentMethod, sale.getPaymentMethod());
|
||||
|
||||
// Display items
|
||||
if (sale.getItems() != null) {
|
||||
binding.llSaleItems.removeAllViews();
|
||||
for (SaleDTO.SaleItemDTO item : sale.getItems()) {
|
||||
addItemRow(item.getProductName(),
|
||||
Math.abs(item.getQuantity()),
|
||||
item.getUnitPrice());
|
||||
}
|
||||
if (sale.getItems() != null) {
|
||||
binding.llSaleItems.removeAllViews();
|
||||
for (SaleDTO.SaleItemDTO item : sale.getItems()) {
|
||||
addItemRow(item.getProductName(), Math.abs(item.getQuantity()), item.getUnitPrice());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,31 +167,46 @@ public class SaleDetailFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
int qty;
|
||||
try {
|
||||
qty = Integer.parseInt(qtyStr);
|
||||
} catch (Exception e) {
|
||||
try { qty = Integer.parseInt(qtyStr); }
|
||||
catch (Exception e) {
|
||||
binding.etSaleQuantity.setError("Invalid quantity");
|
||||
return;
|
||||
}
|
||||
|
||||
ProductDTO product = productList.get(binding.spinnerSaleProduct.getSelectedItemPosition() - 1);
|
||||
ProductDTO product = viewModel.getProductList().getValue().get(binding.spinnerSaleProduct.getSelectedItemPosition() - 1);
|
||||
|
||||
// Check if product already in cart
|
||||
for (SaleDTO.SaleItemDTO existing : cartItems) {
|
||||
for (SaleDTO.SaleItemDTO existing : viewModel.getCartItems().getValue()) {
|
||||
if (existing.getProdId().equals(product.getProdId())) {
|
||||
Toast.makeText(getContext(), "Product already added", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SaleDTO.SaleItemDTO item = new SaleDTO.SaleItemDTO(product.getProdId(), qty);
|
||||
cartItems.add(item);
|
||||
addItemRow(product.getProdName(), qty, product.getProdPrice());
|
||||
updateTotal();
|
||||
viewModel.addToCart(new SaleDTO.SaleItemDTO(product.getProdId(), qty));
|
||||
binding.etSaleQuantity.setText("");
|
||||
});
|
||||
}
|
||||
|
||||
private void renderCartItems() {
|
||||
binding.llSaleItems.removeAllViews();
|
||||
List<SaleDTO.SaleItemDTO> items = viewModel.getCartItems().getValue();
|
||||
List<ProductDTO> products = viewModel.getProductList().getValue();
|
||||
if (items == null || products == null) return;
|
||||
|
||||
for (SaleDTO.SaleItemDTO item : items) {
|
||||
String name = "Unknown";
|
||||
BigDecimal price = BigDecimal.ZERO;
|
||||
for (ProductDTO p : products) {
|
||||
if (p.getProdId().equals(item.getProdId())) {
|
||||
name = p.getProdName();
|
||||
price = p.getProdPrice();
|
||||
break;
|
||||
}
|
||||
}
|
||||
addItemRow(name, item.getQuantity(), price);
|
||||
}
|
||||
}
|
||||
|
||||
private void addItemRow(String name, int qty, BigDecimal price) {
|
||||
if (getContext() == null) return;
|
||||
LinearLayout row = new LinearLayout(getContext());
|
||||
@@ -226,18 +214,15 @@ public class SaleDetailFragment extends Fragment {
|
||||
row.setPadding(0, 8, 0, 8);
|
||||
|
||||
TextView tvName = new TextView(getContext());
|
||||
tvName.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f));
|
||||
tvName.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f));
|
||||
tvName.setText(name);
|
||||
|
||||
TextView tvQty = new TextView(getContext());
|
||||
tvQty.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvQty.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvQty.setText("x" + qty);
|
||||
|
||||
TextView tvPrice = new TextView(getContext());
|
||||
tvPrice.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvPrice.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
||||
tvPrice.setText(price != null ? "$" + price : "");
|
||||
|
||||
row.addView(tvName);
|
||||
@@ -247,16 +232,7 @@ public class SaleDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void updateTotal() {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (SaleDTO.SaleItemDTO item : cartItems) {
|
||||
for (ProductDTO p : productList) {
|
||||
if (p.getProdId().equals(item.getProdId()) && p.getProdPrice() != null) {
|
||||
total = total.add(p.getProdPrice()
|
||||
.multiply(BigDecimal.valueOf(item.getQuantity())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
BigDecimal total = viewModel.calculateSubtotal();
|
||||
binding.tvSaleSubtotal.setText("$" + total);
|
||||
binding.tvSaleDetailTotal.setText("Total: $" + total);
|
||||
}
|
||||
@@ -266,40 +242,28 @@ public class SaleDetailFragment extends Fragment {
|
||||
Toast.makeText(getContext(), "Select a store", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (cartItems.isEmpty()) {
|
||||
if (viewModel.getCartItems().getValue() == null || viewModel.getCartItems().getValue().isEmpty()) {
|
||||
Toast.makeText(getContext(), "Add at least one item", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
DropdownDTO store = storeList.get(binding.spinnerSaleStore.getSelectedItemPosition() - 1);
|
||||
DropdownDTO store = viewModel.getStoreList().getValue().get(binding.spinnerSaleStore.getSelectedItemPosition() - 1);
|
||||
String payment = PAYMENT_METHODS[binding.spinnerPaymentMethod.getSelectedItemPosition()];
|
||||
|
||||
// Optional customer
|
||||
Long customerId = null;
|
||||
if (binding.spinnerSaleCustomer.getSelectedItemPosition() > 0) {
|
||||
customerId = customerList.get(binding.spinnerSaleCustomer.getSelectedItemPosition() - 1)
|
||||
.getId();
|
||||
customerId = viewModel.getCustomerList().getValue().get(binding.spinnerSaleCustomer.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
SaleDTO dto = new SaleDTO(
|
||||
store.getId(),
|
||||
payment,
|
||||
cartItems,
|
||||
false,
|
||||
null,
|
||||
customerId);
|
||||
SaleDTO dto = new SaleDTO(store.getId(), payment, viewModel.getCartItems().getValue(), false, null, customerId);
|
||||
|
||||
saleViewModel.createSale(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.createSale(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
switch (resource.status) {
|
||||
case SUCCESS:
|
||||
Toast.makeText(getContext(), "Sale saved!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
break;
|
||||
case ERROR:
|
||||
Log.e("SALE_SAVE", "Error: " + resource.message);
|
||||
DialogUtils.showInfoDialog(requireContext(), "Save Error", resource.message);
|
||||
break;
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Sale saved!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
DialogUtils.showInfoDialog(requireContext(), "Save Error", resource.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -309,7 +273,7 @@ public class SaleDetailFragment extends Fragment {
|
||||
DialogUtils.showConfirmDialog(requireContext(), "Process Refund",
|
||||
"Are you sure you want to process a refund for this sale?", () -> {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("saleId", saleId);
|
||||
args.putLong("saleId", viewModel.getSaleId());
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_refund, args);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.example.petstoremobile.utils.ActivityLogger;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.viewmodels.ServiceViewModel;
|
||||
import com.example.petstoremobile.viewmodels.ServiceDetailViewModel;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@@ -31,15 +31,12 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class ServiceDetailFragment extends Fragment {
|
||||
|
||||
private FragmentServiceDetailBinding binding;
|
||||
private long serviceId;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private ServiceViewModel viewModel;
|
||||
private ServiceDetailViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ServiceViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(ServiceDetailViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,10 +50,8 @@ public class ServiceDetailFragment extends Fragment {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
//get controls from layout and display the view depending on the mode
|
||||
handleArguments();
|
||||
|
||||
//set button click listeners
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveService.setOnClickListener(v -> saveService());
|
||||
binding.btnDeleteService.setOnClickListener(v -> deleteService());
|
||||
@@ -68,63 +63,44 @@ public class ServiceDetailFragment extends Fragment {
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the saving of service data (adding or updating).
|
||||
*/
|
||||
private void saveService() {
|
||||
// Validates all fields using InputValidator
|
||||
if (!InputValidator.isNotEmpty(binding.etServiceName, "Service Name")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etServiceDesc, "Description")) return;
|
||||
if (!InputValidator.isPositiveInteger(binding.etServiceDuration, "Duration")) return;
|
||||
if (!InputValidator.isPositiveDecimal(binding.etServicePrice, "Price")) return;
|
||||
|
||||
//get all the values from the fields
|
||||
String name = binding.etServiceName.getText().toString().trim();
|
||||
String desc = binding.etServiceDesc.getText().toString().trim();
|
||||
int duration = Integer.parseInt(binding.etServiceDuration.getText().toString().trim());
|
||||
double price = Double.parseDouble(binding.etServicePrice.getText().toString().trim());
|
||||
|
||||
//create a service object to send to the API
|
||||
ServiceDTO serviceDTO = new ServiceDTO();
|
||||
serviceDTO.setServiceName(name);
|
||||
serviceDTO.setServiceDesc(desc);
|
||||
serviceDTO.setServiceDuration(duration);
|
||||
serviceDTO.setServicePrice(price);
|
||||
|
||||
//check if the service is being edited or added
|
||||
if (isEditing) {
|
||||
// Update existing service
|
||||
serviceDTO.setServiceId(serviceId);
|
||||
viewModel.updateService(serviceId, serviceDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Service", "UPDATED", (int) serviceId);
|
||||
viewModel.saveService(serviceDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (viewModel.isEditing()) {
|
||||
ActivityLogger.logChange(requireContext(), "Service", "UPDATED", (int) viewModel.getServiceId());
|
||||
Toast.makeText(getContext(), "Service updated successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
viewModel.createService(serviceDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
} else {
|
||||
ActivityLogger.log(requireContext(), "Added new Service: " + name);
|
||||
Toast.makeText(getContext(), "Service added successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog and handles the deletion of a service.
|
||||
*/
|
||||
private void deleteService() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Service", () ->
|
||||
viewModel.deleteService(serviceId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteService().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Service", "DELETED", (int) serviceId);
|
||||
ActivityLogger.logChange(requireContext(), "Service", "DELETED", (int) viewModel.getServiceId());
|
||||
Toast.makeText(getContext(), "Service deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -133,30 +109,20 @@ public class ServiceDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments passed to the fragment to determine if it's in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
// Service is being edited if the bundle contains a serviceId
|
||||
if (getArguments() != null && getArguments().containsKey("serviceId")) {
|
||||
// Get service data from arguments and populate fields
|
||||
isEditing = true;
|
||||
serviceId = getArguments().getLong("serviceId");
|
||||
long serviceId = getArguments().getLong("serviceId");
|
||||
viewModel.setServiceId(serviceId);
|
||||
binding.tvMode.setText("Edit Service");
|
||||
binding.tvServiceId.setText("ID: " + serviceId);
|
||||
binding.btnDeleteService.setVisibility(View.VISIBLE);
|
||||
loadServiceData();
|
||||
} else {
|
||||
// Service is being added
|
||||
// Set default values for add a new service
|
||||
isEditing = false;
|
||||
viewModel.setServiceId(-1);
|
||||
binding.tvMode.setText("Add Service");
|
||||
binding.tvServiceId.setVisibility(View.GONE);
|
||||
binding.btnDeleteService.setVisibility(View.GONE);
|
||||
@@ -164,11 +130,8 @@ public class ServiceDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific service details from the backend using the ID.
|
||||
*/
|
||||
private void loadServiceData() {
|
||||
viewModel.getServiceById(serviceId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadService().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ServiceDTO s = resource.data;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -12,16 +11,15 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentStaffDetailBinding;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.viewmodels.EmployeeViewModel;
|
||||
import com.example.petstoremobile.viewmodels.StaffDetailViewModel;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class StaffDetailFragment extends Fragment {
|
||||
|
||||
private FragmentStaffDetailBinding binding;
|
||||
private EmployeeViewModel employeeViewModel;
|
||||
private long employeeId = -1;
|
||||
private boolean isEditing = false;
|
||||
private StaffDetailViewModel viewModel;
|
||||
|
||||
private final String[] ROLES = {"STAFF", "ADMIN"};
|
||||
private final String[] STATUSES = {"Active", "Inactive"};
|
||||
@@ -30,7 +28,7 @@ public class StaffDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentStaffDetailBinding.inflate(inflater, container, false);
|
||||
employeeViewModel = new ViewModelProvider(this).get(EmployeeViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(StaffDetailViewModel.class);
|
||||
|
||||
setupSpinners();
|
||||
handleArguments();
|
||||
@@ -51,8 +49,8 @@ public class StaffDetailFragment extends Fragment {
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.getBoolean("isEditing", false)) {
|
||||
isEditing = true;
|
||||
employeeId = a.getLong("employeeId", -1);
|
||||
long employeeId = a.getLong("employeeId", -1);
|
||||
viewModel.setEmployeeId(employeeId, true);
|
||||
|
||||
binding.tvStaffMode.setText("Edit Staff Account");
|
||||
binding.tvStaffId.setText("ID: " + employeeId);
|
||||
@@ -64,7 +62,6 @@ public class StaffDetailFragment extends Fragment {
|
||||
binding.etStaffPhone.setText(a.getString("phone", ""));
|
||||
binding.btnDeleteStaff.setVisibility(View.VISIBLE);
|
||||
|
||||
// Pre-fill role
|
||||
String role = a.getString("role", "STAFF");
|
||||
for (int i = 0; i < ROLES.length; i++) {
|
||||
if (ROLES[i].equals(role)) {
|
||||
@@ -73,13 +70,11 @@ public class StaffDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-fill status
|
||||
boolean active = a.getBoolean("active", true);
|
||||
binding.spinnerStaffStatus.setSelection(active ? 0 : 1);
|
||||
|
||||
} else {
|
||||
isEditing = false;
|
||||
employeeId = -1;
|
||||
viewModel.setEmployeeId(-1, false);
|
||||
binding.tvStaffMode.setText("Add Staff Account");
|
||||
binding.btnDeleteStaff.setVisibility(View.GONE);
|
||||
binding.tvStaffId.setVisibility(View.GONE);
|
||||
@@ -97,12 +92,11 @@ public class StaffDetailFragment extends Fragment {
|
||||
String role = ROLES[binding.spinnerStaffRole.getSelectedItemPosition()];
|
||||
boolean active = binding.spinnerStaffStatus.getSelectedItemPosition() == 0;
|
||||
|
||||
// Validation
|
||||
if (username.isEmpty()) { binding.etStaffUsername.setError("Required"); return; }
|
||||
if (!isEditing && password.isEmpty()) {
|
||||
if (!viewModel.isEditing() && password.isEmpty()) {
|
||||
binding.etStaffPassword.setError("Required for new account"); return;
|
||||
}
|
||||
if (!isEditing && password.length() < 6) {
|
||||
if (!viewModel.isEditing() && password.length() < 6) {
|
||||
binding.etStaffPassword.setError("At least 6 characters"); return;
|
||||
}
|
||||
if (firstName.isEmpty()) { binding.etStaffFirstName.setError("Required"); return; }
|
||||
@@ -121,35 +115,16 @@ public class StaffDetailFragment extends Fragment {
|
||||
active
|
||||
);
|
||||
|
||||
if (isEditing && employeeId > 0) {
|
||||
employeeViewModel.updateEmployee(employeeId, dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
switch (resource.status) {
|
||||
case SUCCESS:
|
||||
Toast.makeText(getContext(), "Updated successfully", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
break;
|
||||
case ERROR:
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
}
|
||||
viewModel.saveEmployee(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Updated successfully" : "Staff account created", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
employeeViewModel.createEmployee(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
switch (resource.status) {
|
||||
case SUCCESS:
|
||||
Toast.makeText(getContext(), "Staff account created", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
break;
|
||||
case ERROR:
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void confirmDelete() {
|
||||
@@ -157,16 +132,13 @@ public class StaffDetailFragment extends Fragment {
|
||||
.setTitle("Delete Staff Account?")
|
||||
.setMessage("This will permanently delete this staff account.")
|
||||
.setPositiveButton("Yes", (d, w) ->
|
||||
employeeViewModel.deleteEmployee(employeeId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteEmployee().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
switch (resource.status) {
|
||||
case SUCCESS:
|
||||
navigateBack();
|
||||
break;
|
||||
case ERROR:
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.SupplierViewModel;
|
||||
import com.example.petstoremobile.viewmodels.SupplierDetailViewModel;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@@ -31,15 +31,12 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class SupplierDetailFragment extends Fragment {
|
||||
|
||||
private FragmentSupplierDetailBinding binding;
|
||||
private long supId;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private SupplierViewModel viewModel;
|
||||
private SupplierDetailViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(SupplierViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(SupplierDetailViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,12 +50,9 @@ public class SupplierDetailFragment extends Fragment {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Add phone number formatting (CA) and limit length to 14 characters
|
||||
UIUtils.formatPhoneInput(binding.etSupPhone);
|
||||
|
||||
handleArguments();
|
||||
|
||||
//set button click listeners
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveSupplier.setOnClickListener(v -> saveSupplier());
|
||||
binding.btnDeleteSupplier.setOnClickListener(v -> deleteSupplier());
|
||||
@@ -70,25 +64,19 @@ public class SupplierDetailFragment extends Fragment {
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the saving of supplier data (adding or updating).
|
||||
*/
|
||||
private void saveSupplier() {
|
||||
// Validates all fields using InputValidator
|
||||
if (!InputValidator.isNotEmpty(binding.etSupCompany, "Company Name")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etSupContactFirstName, "First Name")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etSupContactLastName, "Last Name")) return;
|
||||
if (!InputValidator.isValidEmail(binding.etSupEmail)) return;
|
||||
if (!InputValidator.isValidPhone(binding.etSupPhone)) return;
|
||||
|
||||
//get all the values from the fields
|
||||
String company = binding.etSupCompany.getText().toString().trim();
|
||||
String firstName = binding.etSupContactFirstName.getText().toString().trim();
|
||||
String lastName = binding.etSupContactLastName.getText().toString().trim();
|
||||
String email = binding.etSupEmail.getText().toString().trim();
|
||||
String phone = binding.etSupPhone.getText().toString().trim();
|
||||
|
||||
//create a supplier object to send to the API
|
||||
SupplierDTO supplierDTO = new SupplierDTO();
|
||||
supplierDTO.setSupCompany(company);
|
||||
supplierDTO.setSupContactFirstName(firstName);
|
||||
@@ -96,41 +84,27 @@ public class SupplierDetailFragment extends Fragment {
|
||||
supplierDTO.setSupEmail(email);
|
||||
supplierDTO.setSupPhone(phone);
|
||||
|
||||
//check if the supplier is being edited or added
|
||||
if (isEditing) {
|
||||
// Update existing supplier
|
||||
supplierDTO.setSupId(supId);
|
||||
viewModel.updateSupplier(supId, supplierDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "UPDATED", (int) supId);
|
||||
viewModel.saveSupplier(supplierDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (viewModel.isEditing()) {
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "UPDATED", (int) viewModel.getSupId());
|
||||
Toast.makeText(getContext(), "Supplier updated successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Add new supplier
|
||||
viewModel.createSupplier(supplierDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
} else {
|
||||
ActivityLogger.log(requireContext(), "Added new Supplier: " + company);
|
||||
Toast.makeText(getContext(), "Supplier added successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog and handles the deletion of a supplier.
|
||||
*/
|
||||
private void deleteSupplier() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Supplier", () ->
|
||||
viewModel.deleteSupplier(supId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "DELETED", (int) supId);
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "DELETED", (int) viewModel.getSupId());
|
||||
Toast.makeText(getContext(), "Supplier deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -139,31 +113,21 @@ public class SupplierDetailFragment extends Fragment {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments passed to the fragment to determine if it's in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
// Supplier is being edited if the bundle contains a supId
|
||||
if (getArguments() != null && getArguments().containsKey("supId")) {
|
||||
// Get supplier data from arguments and populate fields
|
||||
isEditing = true;
|
||||
supId = getArguments().getLong("supId");
|
||||
long supId = getArguments().getLong("supId");
|
||||
viewModel.setSupId(supId);
|
||||
binding.tvMode.setText("Edit Supplier");
|
||||
binding.tvSupId.setText("ID: " + supId);
|
||||
binding.tvSupId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeleteSupplier.setVisibility(View.VISIBLE);
|
||||
loadSupplierData();
|
||||
} else {
|
||||
// Supplier is being added
|
||||
// Set default values for add a new supplier
|
||||
isEditing = false;
|
||||
viewModel.setSupId(-1);
|
||||
binding.tvMode.setText("Add Supplier");
|
||||
binding.tvSupId.setVisibility(View.GONE);
|
||||
binding.btnDeleteSupplier.setVisibility(View.GONE);
|
||||
@@ -171,11 +135,8 @@ public class SupplierDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific supplier details from the backend using the ID.
|
||||
*/
|
||||
private void loadSupplierData() {
|
||||
viewModel.getSupplierById(supId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.loadSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
SupplierDTO s = resource.data;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.AdoptionDTO;
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.repositories.AdoptionRepository;
|
||||
import com.example.petstoremobile.repositories.CustomerRepository;
|
||||
import com.example.petstoremobile.repositories.PetRepository;
|
||||
import com.example.petstoremobile.repositories.StoreRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class AdoptionDetailViewModel extends ViewModel {
|
||||
private final AdoptionRepository adoptionRepository;
|
||||
private final PetRepository petRepository;
|
||||
private final CustomerRepository customerRepository;
|
||||
private final StoreRepository storeRepository;
|
||||
|
||||
private long adoptionId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private final MutableLiveData<List<DropdownDTO>> petList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<DropdownDTO>> customerList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<DropdownDTO>> storeList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<DropdownDTO>> employeeList = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
@Inject
|
||||
public AdoptionDetailViewModel(AdoptionRepository adoptionRepository, PetRepository petRepository,
|
||||
CustomerRepository customerRepository, StoreRepository storeRepository) {
|
||||
this.adoptionRepository = adoptionRepository;
|
||||
this.petRepository = petRepository;
|
||||
this.customerRepository = customerRepository;
|
||||
this.storeRepository = storeRepository;
|
||||
}
|
||||
|
||||
public void setAdoptionId(long id) {
|
||||
this.adoptionId = id;
|
||||
this.isEditing = id != -1;
|
||||
}
|
||||
|
||||
public long getAdoptionId() {
|
||||
return adoptionId;
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
}
|
||||
|
||||
public LiveData<Resource<AdoptionDTO>> loadAdoption() {
|
||||
return adoptionRepository.getAdoptionById(adoptionId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadPets() {
|
||||
return petRepository.getAdoptionPets();
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadCustomers() {
|
||||
return customerRepository.getCustomerDropdowns();
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadStores() {
|
||||
return storeRepository.getStoreDropdowns();
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadEmployees(Long storeId) {
|
||||
return storeRepository.getStoreEmployees(storeId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<AdoptionDTO>> saveAdoption(AdoptionDTO dto) {
|
||||
if (isEditing) {
|
||||
return adoptionRepository.updateAdoption(adoptionId, dto);
|
||||
} else {
|
||||
return adoptionRepository.createAdoption(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteAdoption() {
|
||||
return adoptionRepository.deleteAdoption(adoptionId);
|
||||
}
|
||||
|
||||
public void setPetList(List<DropdownDTO> list) { petList.setValue(list); }
|
||||
public LiveData<List<DropdownDTO>> getPetList() { return petList; }
|
||||
|
||||
public void setCustomerList(List<DropdownDTO> list) { customerList.setValue(list); }
|
||||
public LiveData<List<DropdownDTO>> getCustomerList() { return customerList; }
|
||||
|
||||
public void setStoreList(List<DropdownDTO> list) { storeList.setValue(list); }
|
||||
public LiveData<List<DropdownDTO>> getStoreList() { return storeList; }
|
||||
|
||||
public void setEmployeeList(List<DropdownDTO> list) { employeeList.setValue(list); }
|
||||
public LiveData<List<DropdownDTO>> getEmployeeList() { return employeeList; }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.repositories.InventoryRepository;
|
||||
import com.example.petstoremobile.repositories.ProductRepository;
|
||||
import com.example.petstoremobile.repositories.StoreRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class InventoryDetailViewModel extends ViewModel {
|
||||
private final InventoryRepository inventoryRepository;
|
||||
private final StoreRepository storeRepository;
|
||||
private final ProductRepository productRepository;
|
||||
|
||||
private long inventoryId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private final MutableLiveData<List<DropdownDTO>> storeList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<ProductDTO>> productList = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
@Inject
|
||||
public InventoryDetailViewModel(InventoryRepository inventoryRepository, StoreRepository storeRepository, ProductRepository productRepository) {
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.storeRepository = storeRepository;
|
||||
this.productRepository = productRepository;
|
||||
}
|
||||
|
||||
public void setInventoryId(long id) {
|
||||
this.inventoryId = id;
|
||||
this.isEditing = id != -1;
|
||||
}
|
||||
|
||||
public long getInventoryId() { return inventoryId; }
|
||||
public boolean isEditing() { return isEditing; }
|
||||
|
||||
public LiveData<Resource<InventoryDTO>> loadInventory() {
|
||||
return inventoryRepository.getInventoryById(inventoryId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadStores() {
|
||||
return storeRepository.getStoreDropdowns();
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<ProductDTO>>> loadProducts() {
|
||||
return productRepository.getAllProducts(null, null, 0, 500, "prodName");
|
||||
}
|
||||
|
||||
public LiveData<Resource<InventoryDTO>> saveInventory(InventoryDTO dto) {
|
||||
if (isEditing) {
|
||||
return inventoryRepository.updateInventory(inventoryId, dto);
|
||||
} else {
|
||||
return inventoryRepository.createInventory(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteInventory() {
|
||||
return inventoryRepository.deleteInventory(inventoryId);
|
||||
}
|
||||
|
||||
public void setStoreList(List<DropdownDTO> list) { storeList.setValue(list); }
|
||||
public LiveData<List<DropdownDTO>> getStoreList() { return storeList; }
|
||||
|
||||
public void setProductList(List<ProductDTO> list) { productList.setValue(list); }
|
||||
public LiveData<List<ProductDTO>> getProductList() { return productList; }
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.PetDTO;
|
||||
import com.example.petstoremobile.repositories.CustomerRepository;
|
||||
import com.example.petstoremobile.repositories.PetRepository;
|
||||
import com.example.petstoremobile.repositories.StoreRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class PetDetailViewModel extends ViewModel {
|
||||
private final PetRepository petRepository;
|
||||
private final CustomerRepository customerRepository;
|
||||
private final StoreRepository storeRepository;
|
||||
|
||||
private final MutableLiveData<PetDTO> petState = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<DropdownDTO>> customerList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<DropdownDTO>> storeList = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
private long petId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Inject
|
||||
public PetDetailViewModel(PetRepository petRepository, CustomerRepository customerRepository, StoreRepository storeRepository) {
|
||||
this.petRepository = petRepository;
|
||||
this.customerRepository = customerRepository;
|
||||
this.storeRepository = storeRepository;
|
||||
}
|
||||
|
||||
public void setPetId(long id) {
|
||||
this.petId = id;
|
||||
this.isEditing = id != -1;
|
||||
}
|
||||
|
||||
public long getPetId() {
|
||||
return petId;
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PetDTO>> loadPet() {
|
||||
return petRepository.getPetById(petId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadCustomers() {
|
||||
return customerRepository.getCustomerDropdowns();
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadStores() {
|
||||
return storeRepository.getStoreDropdowns();
|
||||
}
|
||||
|
||||
public LiveData<Resource<PetDTO>> savePet(PetDTO petDTO) {
|
||||
if (isEditing) {
|
||||
petDTO.setPetId(petId);
|
||||
return petRepository.updatePet(petId, petDTO);
|
||||
} else {
|
||||
return petRepository.createPet(petDTO);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deletePet() {
|
||||
return petRepository.deletePet(petId);
|
||||
}
|
||||
|
||||
public void setCustomerList(List<DropdownDTO> list) {
|
||||
customerList.setValue(list);
|
||||
}
|
||||
|
||||
public LiveData<List<DropdownDTO>> getCustomerList() {
|
||||
return customerList;
|
||||
}
|
||||
|
||||
public void setStoreList(List<DropdownDTO> list) {
|
||||
storeList.setValue(list);
|
||||
}
|
||||
|
||||
public LiveData<List<DropdownDTO>> getStoreList() {
|
||||
return storeList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.CategoryDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.repositories.CategoryRepository;
|
||||
import com.example.petstoremobile.repositories.ProductRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
import okhttp3.MultipartBody;
|
||||
|
||||
@HiltViewModel
|
||||
public class ProductDetailViewModel extends ViewModel {
|
||||
private final ProductRepository productRepository;
|
||||
private final CategoryRepository categoryRepository;
|
||||
|
||||
private final MutableLiveData<List<CategoryDTO>> categoryList = new MutableLiveData<>(new ArrayList<>());
|
||||
private long prodId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Inject
|
||||
public ProductDetailViewModel(ProductRepository productRepository, CategoryRepository categoryRepository) {
|
||||
this.productRepository = productRepository;
|
||||
this.categoryRepository = categoryRepository;
|
||||
}
|
||||
|
||||
public void setProdId(long id) {
|
||||
this.prodId = id;
|
||||
this.isEditing = id != -1;
|
||||
}
|
||||
|
||||
public long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<CategoryDTO>>> loadCategories() {
|
||||
return categoryRepository.getAllCategories(0, 100);
|
||||
}
|
||||
|
||||
public LiveData<Resource<ProductDTO>> loadProduct() {
|
||||
return productRepository.getProductById(prodId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<ProductDTO>> saveProduct(ProductDTO dto) {
|
||||
if (isEditing) {
|
||||
return productRepository.updateProduct(prodId, dto);
|
||||
} else {
|
||||
return productRepository.createProduct(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteProduct() {
|
||||
return productRepository.deleteProduct(prodId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> uploadProductImage(MultipartBody.Part image) {
|
||||
return productRepository.uploadProductImage(prodId, image);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteProductImage() {
|
||||
return productRepository.deleteProductImage(prodId);
|
||||
}
|
||||
|
||||
public void setCategoryList(List<CategoryDTO> list) {
|
||||
categoryList.setValue(list);
|
||||
}
|
||||
|
||||
public LiveData<List<CategoryDTO>> getCategoryList() {
|
||||
return categoryList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.dtos.ProductSupplierDTO;
|
||||
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||
import com.example.petstoremobile.repositories.ProductRepository;
|
||||
import com.example.petstoremobile.repositories.ProductSupplierRepository;
|
||||
import com.example.petstoremobile.repositories.SupplierRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class ProductSupplierDetailViewModel extends ViewModel {
|
||||
private final ProductSupplierRepository psRepository;
|
||||
private final ProductRepository productRepository;
|
||||
private final SupplierRepository supplierRepository;
|
||||
|
||||
private boolean isEditing = false;
|
||||
private long editProductId = -1;
|
||||
private long editSupplierId = -1;
|
||||
|
||||
private final MutableLiveData<List<ProductDTO>> productList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<SupplierDTO>> supplierList = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
@Inject
|
||||
public ProductSupplierDetailViewModel(ProductSupplierRepository psRepository, ProductRepository productRepository, SupplierRepository supplierRepository) {
|
||||
this.psRepository = psRepository;
|
||||
this.productRepository = productRepository;
|
||||
this.supplierRepository = supplierRepository;
|
||||
}
|
||||
|
||||
public void setEditMode(long productId, long supplierId) {
|
||||
this.isEditing = true;
|
||||
this.editProductId = productId;
|
||||
this.editSupplierId = supplierId;
|
||||
}
|
||||
|
||||
public boolean isEditing() { return isEditing; }
|
||||
public long getEditProductId() { return editProductId; }
|
||||
public long getEditSupplierId() { return editSupplierId; }
|
||||
|
||||
public LiveData<Resource<PageResponse<ProductDTO>>> loadProducts() {
|
||||
return productRepository.getAllProducts(null, null, 0, 200, "prodName");
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<SupplierDTO>>> loadSuppliers() {
|
||||
return supplierRepository.getAllSuppliers(0, 200, null, "supCompany");
|
||||
}
|
||||
|
||||
public LiveData<Resource<ProductSupplierDTO>> saveProductSupplier(ProductSupplierDTO dto) {
|
||||
if (isEditing) {
|
||||
return psRepository.updateProductSupplier(editProductId, editSupplierId, dto);
|
||||
} else {
|
||||
return psRepository.createProductSupplier(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteProductSupplier() {
|
||||
return psRepository.deleteProductSupplier(editProductId, editSupplierId);
|
||||
}
|
||||
|
||||
public void setProductList(List<ProductDTO> list) { productList.setValue(list); }
|
||||
public LiveData<List<ProductDTO>> getProductList() { return productList; }
|
||||
|
||||
public void setSupplierList(List<SupplierDTO> list) { supplierList.setValue(list); }
|
||||
public LiveData<List<SupplierDTO>> getSupplierList() { return supplierList; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
|
||||
import com.example.petstoremobile.repositories.PurchaseOrderRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class PurchaseOrderDetailViewModel extends ViewModel {
|
||||
private final PurchaseOrderRepository repository;
|
||||
|
||||
@Inject
|
||||
public PurchaseOrderDetailViewModel(PurchaseOrderRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PurchaseOrderDTO>> loadPurchaseOrder(long id) {
|
||||
return repository.getPurchaseOrderById(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.repositories.SaleRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class RefundViewModel extends ViewModel {
|
||||
private final SaleRepository saleRepository;
|
||||
|
||||
private final MutableLiveData<List<SaleDTO>> allSales = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<SaleDTO> currentSale = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<RefundItem>> availableItems = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<RefundItem>> refundCart = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
@Inject
|
||||
public RefundViewModel(SaleRepository saleRepository) {
|
||||
this.saleRepository = saleRepository;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<SaleDTO>>> loadAllSales() {
|
||||
return saleRepository.getAllSales(0, 1000, null, null, null, "saleDate,desc");
|
||||
}
|
||||
|
||||
public void setAllSales(List<SaleDTO> sales) {
|
||||
allSales.setValue(sales);
|
||||
}
|
||||
|
||||
public List<SaleDTO> getAllSalesList() {
|
||||
return allSales.getValue();
|
||||
}
|
||||
|
||||
public void setCurrentSale(SaleDTO sale) {
|
||||
currentSale.setValue(sale);
|
||||
buildRefundableItems();
|
||||
}
|
||||
|
||||
public SaleDTO getCurrentSale() {
|
||||
return currentSale.getValue();
|
||||
}
|
||||
|
||||
public LiveData<List<RefundItem>> getAvailableItems() {
|
||||
return availableItems;
|
||||
}
|
||||
|
||||
public LiveData<List<RefundItem>> getRefundCart() {
|
||||
return refundCart;
|
||||
}
|
||||
|
||||
private void buildRefundableItems() {
|
||||
SaleDTO sale = currentSale.getValue();
|
||||
List<SaleDTO> sales = allSales.getValue();
|
||||
if (sale == null || sales == null || sale.getItems() == null) {
|
||||
availableItems.setValue(new ArrayList<>());
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Long, Integer> alreadyRefunded = new HashMap<>();
|
||||
for (SaleDTO s : sales) {
|
||||
if (Boolean.TRUE.equals(s.getIsRefund())
|
||||
&& sale.getSaleId().equals(s.getOriginalSaleId())
|
||||
&& s.getItems() != null) {
|
||||
for (SaleDTO.SaleItemDTO item : s.getItems()) {
|
||||
if (item.getProdId() != null && item.getQuantity() != null) {
|
||||
alreadyRefunded.merge(item.getProdId(),
|
||||
Math.abs(item.getQuantity()), Integer::sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<RefundItem> items = new ArrayList<>();
|
||||
for (SaleDTO.SaleItemDTO item : sale.getItems()) {
|
||||
if (item.getProdId() == null || item.getQuantity() == null) continue;
|
||||
int refunded = alreadyRefunded.getOrDefault(item.getProdId(), 0);
|
||||
int remaining = item.getQuantity() - refunded;
|
||||
if (remaining > 0) {
|
||||
items.add(new RefundItem(
|
||||
item.getProdId(),
|
||||
item.getProductName() != null ? item.getProductName() : "Unknown",
|
||||
remaining,
|
||||
item.getUnitPrice()
|
||||
));
|
||||
}
|
||||
}
|
||||
availableItems.setValue(items);
|
||||
refundCart.setValue(new ArrayList<>());
|
||||
}
|
||||
|
||||
public void addToCart(RefundItem item, int qty) {
|
||||
List<RefundItem> cart = new ArrayList<>(refundCart.getValue());
|
||||
boolean merged = false;
|
||||
for (int i = 0; i < cart.size(); i++) {
|
||||
if (cart.get(i).prodId == item.prodId) {
|
||||
RefundItem existing = cart.get(i);
|
||||
cart.set(i, new RefundItem(existing.prodId, existing.productName, existing.quantity + qty, existing.unitPrice));
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!merged) {
|
||||
cart.add(new RefundItem(item.prodId, item.productName, qty, item.unitPrice));
|
||||
}
|
||||
refundCart.setValue(cart);
|
||||
}
|
||||
|
||||
public void removeFromCart(RefundItem item) {
|
||||
List<RefundItem> cart = new ArrayList<>(refundCart.getValue());
|
||||
cart.remove(item);
|
||||
refundCart.setValue(cart);
|
||||
}
|
||||
|
||||
public LiveData<Resource<SaleDTO>> submitRefund(String paymentMethod) {
|
||||
SaleDTO sale = currentSale.getValue();
|
||||
List<RefundItem> cart = refundCart.getValue();
|
||||
if (sale == null || cart == null || cart.isEmpty()) return null;
|
||||
|
||||
List<SaleDTO.SaleItemDTO> items = new ArrayList<>();
|
||||
for (RefundItem item : cart) {
|
||||
items.add(new SaleDTO.SaleItemDTO(item.prodId, -item.quantity));
|
||||
}
|
||||
|
||||
SaleDTO dto = new SaleDTO(
|
||||
sale.getStoreId(),
|
||||
paymentMethod,
|
||||
items,
|
||||
true,
|
||||
sale.getSaleId(),
|
||||
null
|
||||
);
|
||||
|
||||
return saleRepository.createSale(dto);
|
||||
}
|
||||
|
||||
public static class RefundItem {
|
||||
public long prodId;
|
||||
public String productName;
|
||||
public int quantity;
|
||||
public BigDecimal unitPrice;
|
||||
|
||||
public RefundItem(long prodId, String productName, int quantity, BigDecimal unitPrice) {
|
||||
this.prodId = prodId;
|
||||
this.productName = productName;
|
||||
this.quantity = quantity;
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
|
||||
public BigDecimal getTotal() {
|
||||
return unitPrice != null ? unitPrice.multiply(BigDecimal.valueOf(quantity)) : BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.repositories.CustomerRepository;
|
||||
import com.example.petstoremobile.repositories.ProductRepository;
|
||||
import com.example.petstoremobile.repositories.SaleRepository;
|
||||
import com.example.petstoremobile.repositories.StoreRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class SaleDetailViewModel extends ViewModel {
|
||||
private final SaleRepository saleRepository;
|
||||
private final StoreRepository storeRepository;
|
||||
private final CustomerRepository customerRepository;
|
||||
private final ProductRepository productRepository;
|
||||
|
||||
private long saleId = -1;
|
||||
private boolean viewOnly = false;
|
||||
|
||||
private final MutableLiveData<List<DropdownDTO>> storeList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<DropdownDTO>> customerList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<ProductDTO>> productList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<SaleDTO.SaleItemDTO>> cartItems = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
@Inject
|
||||
public SaleDetailViewModel(SaleRepository saleRepository, StoreRepository storeRepository,
|
||||
CustomerRepository customerRepository, ProductRepository productRepository) {
|
||||
this.saleRepository = saleRepository;
|
||||
this.storeRepository = storeRepository;
|
||||
this.customerRepository = customerRepository;
|
||||
this.productRepository = productRepository;
|
||||
}
|
||||
|
||||
public void setSaleId(long id, boolean viewOnly) {
|
||||
this.saleId = id;
|
||||
this.viewOnly = viewOnly;
|
||||
}
|
||||
|
||||
public long getSaleId() { return saleId; }
|
||||
public boolean isViewOnly() { return viewOnly; }
|
||||
|
||||
public LiveData<Resource<SaleDTO>> loadSaleDetails() {
|
||||
return saleRepository.getSaleById(saleId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadStores() {
|
||||
return storeRepository.getStoreDropdowns();
|
||||
}
|
||||
|
||||
public LiveData<Resource<List<DropdownDTO>>> loadCustomers() {
|
||||
return customerRepository.getCustomerDropdowns();
|
||||
}
|
||||
|
||||
public LiveData<Resource<com.example.petstoremobile.dtos.PageResponse<ProductDTO>>> loadProducts() {
|
||||
return productRepository.getAllProducts(null, null, 0, 200, null);
|
||||
}
|
||||
|
||||
public LiveData<Resource<SaleDTO>> createSale(SaleDTO sale) {
|
||||
return saleRepository.createSale(sale);
|
||||
}
|
||||
|
||||
public void setStoreList(List<DropdownDTO> list) { storeList.setValue(list); }
|
||||
public LiveData<List<DropdownDTO>> getStoreList() { return storeList; }
|
||||
|
||||
public void setCustomerList(List<DropdownDTO> list) { customerList.setValue(list); }
|
||||
public LiveData<List<DropdownDTO>> getCustomerList() { return customerList; }
|
||||
|
||||
public void setProductList(List<ProductDTO> list) { productList.setValue(list); }
|
||||
public LiveData<List<ProductDTO>> getProductList() { return productList; }
|
||||
|
||||
public void addToCart(SaleDTO.SaleItemDTO item) {
|
||||
List<SaleDTO.SaleItemDTO> currentCart = new ArrayList<>(cartItems.getValue());
|
||||
currentCart.add(item);
|
||||
cartItems.setValue(currentCart);
|
||||
}
|
||||
|
||||
public LiveData<List<SaleDTO.SaleItemDTO>> getCartItems() { return cartItems; }
|
||||
|
||||
public BigDecimal calculateSubtotal() {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
List<SaleDTO.SaleItemDTO> items = cartItems.getValue();
|
||||
List<ProductDTO> products = productList.getValue();
|
||||
if (items != null && products != null) {
|
||||
for (SaleDTO.SaleItemDTO item : items) {
|
||||
for (ProductDTO p : products) {
|
||||
if (p.getProdId().equals(item.getProdId()) && p.getProdPrice() != null) {
|
||||
total = total.add(p.getProdPrice().multiply(BigDecimal.valueOf(item.getQuantity())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.repositories.ServiceRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class ServiceDetailViewModel extends ViewModel {
|
||||
private final ServiceRepository repository;
|
||||
private long serviceId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Inject
|
||||
public ServiceDetailViewModel(ServiceRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public void setServiceId(long id) {
|
||||
this.serviceId = id;
|
||||
this.isEditing = id != -1;
|
||||
}
|
||||
|
||||
public long getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
}
|
||||
|
||||
public LiveData<Resource<ServiceDTO>> loadService() {
|
||||
return repository.getServiceById(serviceId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<ServiceDTO>> saveService(ServiceDTO dto) {
|
||||
if (isEditing) {
|
||||
dto.setServiceId(serviceId);
|
||||
return repository.updateService(serviceId, dto);
|
||||
} else {
|
||||
return repository.createService(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteService() {
|
||||
return repository.deleteService(serviceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.repositories.EmployeeRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class StaffDetailViewModel extends ViewModel {
|
||||
private final EmployeeRepository repository;
|
||||
private long employeeId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Inject
|
||||
public StaffDetailViewModel(EmployeeRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public void setEmployeeId(long id, boolean isEditing) {
|
||||
this.employeeId = id;
|
||||
this.isEditing = isEditing;
|
||||
}
|
||||
|
||||
public long getEmployeeId() {
|
||||
return employeeId;
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
}
|
||||
|
||||
public LiveData<Resource<EmployeeDTO>> saveEmployee(EmployeeDTO dto) {
|
||||
if (isEditing && employeeId > 0) {
|
||||
return repository.updateEmployee(employeeId, dto);
|
||||
} else {
|
||||
return repository.createEmployee(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteEmployee() {
|
||||
return repository.deleteEmployee(employeeId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||
import com.example.petstoremobile.repositories.SupplierRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class SupplierDetailViewModel extends ViewModel {
|
||||
private final SupplierRepository repository;
|
||||
private long supId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Inject
|
||||
public SupplierDetailViewModel(SupplierRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public void setSupId(long id) {
|
||||
this.supId = id;
|
||||
this.isEditing = id != -1;
|
||||
}
|
||||
|
||||
public long getSupId() {
|
||||
return supId;
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
}
|
||||
|
||||
public LiveData<Resource<SupplierDTO>> loadSupplier() {
|
||||
return repository.getSupplierById(supId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<SupplierDTO>> saveSupplier(SupplierDTO dto) {
|
||||
if (isEditing) {
|
||||
dto.setSupId(supId);
|
||||
return repository.updateSupplier(supId, dto);
|
||||
} else {
|
||||
return repository.createSupplier(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteSupplier() {
|
||||
return repository.deleteSupplier(supId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user