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