/*
* The contents of this file are subject to the OpenMRS Public License
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* Copyright (C) OpenHMIS. All Rights Reserved.
*/
package org.openmrs.module.webservices.rest.resource;
import com.google.common.collect.Iterators;
import org.openmrs.Provider;
import org.openmrs.User;
import org.openmrs.api.AdministrationService;
import org.openmrs.api.ProviderService;
import org.openmrs.api.context.Context;
import org.openmrs.module.openhmis.cashier.ModuleSettings;
import org.openmrs.module.openhmis.cashier.api.IBillService;
import org.openmrs.module.openhmis.cashier.api.ITimesheetService;
import org.openmrs.module.openhmis.cashier.api.model.Bill;
import org.openmrs.module.openhmis.cashier.api.model.BillLineItem;
import org.openmrs.module.openhmis.cashier.api.model.BillStatus;
import org.openmrs.module.openhmis.cashier.api.model.Payment;
import org.openmrs.module.openhmis.cashier.api.model.CashPoint;
import org.openmrs.module.openhmis.cashier.api.model.Timesheet;
import org.openmrs.module.openhmis.cashier.api.util.RoundingUtil;
import org.openmrs.module.openhmis.commons.api.entity.IEntityDataService;
import org.openmrs.module.webservices.rest.web.RestConstants;
import org.openmrs.module.webservices.rest.web.annotation.PropertySetter;
import org.openmrs.module.webservices.rest.web.annotation.Resource;
import org.openmrs.module.webservices.rest.web.representation.DefaultRepresentation;
import org.openmrs.module.webservices.rest.web.representation.RefRepresentation;
import org.openmrs.module.webservices.rest.web.representation.Representation;
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription;
import org.springframework.web.client.RestClientException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* REST resource representing a {@link Bill}.
*/
@Resource(name = RestConstants.VERSION_2 + "/cashier/bill", supportedClass = Bill.class,
supportedOpenmrsVersions = { "1.9.*", "1.10.*", "1.11.*", "1.12.*" })
public class BillResource extends BaseRestDataResource<Bill> {
@Override
public DelegatingResourceDescription getRepresentationDescription(Representation rep) {
DelegatingResourceDescription description = super.getRepresentationDescription(rep);
if (!(rep instanceof RefRepresentation)) {
description.addProperty("adjustedBy", Representation.REF);
description.addProperty("billAdjusted", Representation.REF);
description.addProperty("cashPoint", Representation.REF);
description.addProperty("cashier", Representation.REF);
description.addProperty("dateCreated");
description.addProperty("lineItems");
description.addProperty("patient", Representation.REF);
description.addProperty("payments", Representation.FULL);
description.addProperty("receiptNumber");
description.addProperty("status");
description.addProperty("adjustmentReason");
description.addProperty("id");
}
return description;
}
@Override
public DelegatingResourceDescription getCreatableProperties() {
return getRepresentationDescription(new DefaultRepresentation());
}
@PropertySetter("lineItems")
public void setBillLineItems(Bill instance, List<BillLineItem> lineItems) {
if (instance.getLineItems() == null) {
instance.setLineItems(new ArrayList<BillLineItem>(lineItems.size()));
}
BaseRestDataResource.syncCollection(instance.getLineItems(), lineItems);
for (BillLineItem item : instance.getLineItems()) {
item.setBill(instance);
}
}
@PropertySetter("payments")
public void setBillPayments(Bill instance, Set<Payment> payments) {
if (instance.getPayments() == null) {
instance.setPayments(new HashSet<Payment>(payments.size()));
}
BaseRestDataResource.syncCollection(instance.getPayments(), payments);
for (Payment payment : instance.getPayments()) {
instance.addPayment(payment);
}
}
@PropertySetter("billAdjusted")
public void setBillAdjusted(Bill instance, Bill billAdjusted) {
billAdjusted.addAdjustedBy(instance);
instance.setBillAdjusted(billAdjusted);
}
@PropertySetter("status")
public void setBillStatus(Bill instance, BillStatus status) {
if (instance.getStatus() == null) {
instance.setStatus(status);
} else if (instance.getStatus() == BillStatus.PENDING && status == BillStatus.POSTED) {
instance.setStatus(status);
}
if (status == BillStatus.POSTED) {
RoundingUtil.handleRoundingLineItem(instance);
}
}
@PropertySetter("adjustmentReason")
public void setAdjustReason(Bill instance, String adjustReason) {
if (instance.getBillAdjusted().getUuid() != null) {
instance.getBillAdjusted().setAdjustmentReason(adjustReason);
}
}
@Override
public Bill save(Bill bill) {
//TODO: Test all the ways that this could fail
if (bill.getId() == null) {
if (bill.getCashier() == null) {
Provider cashier = getCurrentCashier(bill);
if (cashier == null) {
throw new RestClientException("Couldn't find Provider for the current user ("
+ Context.getAuthenticatedUser().getName() + ")");
}
bill.setCashier(cashier);
}
if (bill.getCashPoint() == null) {
loadBillCashPoint(bill);
}
// Now that all all attributes have been set (i.e., payments and bill status) we can check to see if the bill
// is fully paid.
bill.checkPaidAndUpdateStatus();
if (bill.getStatus() == null) {
bill.setStatus(BillStatus.PENDING);
}
}
return super.save(bill);
}
@SuppressWarnings("unchecked")
@Override
public Class<IEntityDataService<Bill>> getServiceClass() {
return (Class<IEntityDataService<Bill>>)(Object)IBillService.class;
}
public String getDisplayString(Bill instance) {
return instance.getReceiptNumber();
}
@Override
public Bill newDelegate() {
return new Bill();
}
private Provider getCurrentCashier(Bill bill) {
User currentUser = Context.getAuthenticatedUser();
ProviderService service = Context.getProviderService();
Collection<Provider> providers = service.getProvidersByPerson(currentUser.getPerson());
return Iterators.get(providers.iterator(), 0, null);
}
private void loadBillCashPoint(Bill bill) {
ITimesheetService service = Context.getService(ITimesheetService.class);
Timesheet timesheet = service.getCurrentTimesheet(bill.getCashier());
if (timesheet == null) {
AdministrationService adminService = Context.getAdministrationService();
boolean timesheetRequired;
try {
timesheetRequired =
Boolean.parseBoolean(adminService.getGlobalProperty(ModuleSettings.TIMESHEET_REQUIRED_PROPERTY));
} catch (Exception e) {
timesheetRequired = false;
}
if (timesheetRequired) {
throw new RestClientException("A current timesheet does not exist for cashier " + bill.getCashier());
} else if (bill.getBillAdjusted() != null) {
// If this is an adjusting bill, copy cash point from billAdjusted
bill.setCashPoint(bill.getBillAdjusted().getCashPoint());
} else {
throw new RestClientException("Cash point cannot be null!");
}
} else {
CashPoint cashPoint = timesheet.getCashPoint();
if (cashPoint == null) {
throw new RestClientException("No cash points defined for the current timesheet!");
}
bill.setCashPoint(cashPoint);
}
}
}