/*******************************************************************************
* Copyright (c) 2006-2011, G. Weirich and Elexis
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* G. Weirich - initial implementation
*
*******************************************************************************/
package ch.elexis.data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import ch.elexis.core.data.activator.CoreHub;
import ch.elexis.core.data.constants.ExtensionPointConstantsData;
import ch.elexis.core.data.interfaces.IVerrechenbar;
import ch.elexis.core.data.interfaces.IVerrechnetAdjuster;
import ch.elexis.core.data.service.StockService;
import ch.elexis.core.data.util.Extensions;
import ch.elexis.core.model.verrechnet.Constants;
import ch.rgw.tools.ExHandler;
import ch.rgw.tools.Money;
import ch.rgw.tools.Result;
import ch.rgw.tools.TimeTool;
/**
* Ein Verrechnet ist ein realisiertes Verrechenbar. Ein Verrechenbar wird durch die Zuordnung zu
* einer Konsultation zu einem Verrechnet. Der Preis eines Verrechnet ist zunächst Taxpunkwert(TP)
* mal Scale (Immer in der kleinsten Währungseinheit, also Rappen oder ggf. cent). Der effektive
* Preis kann aber geändert werden (Rabatt etc.) Nebst VK_Scale, welche in der Schweiz dem
* taxpunktwert entspricht, können noch externe und interne zusätzlich Skalierungen angewendet
* werden. PrimaryScalefactor wird beispielsweise für %-Reduktionen oder Zusschläge gemäss Tarmed
* verwendet, SecondaryScalefactor kann ein Rabatt oder ein Privatzuschschlag sein.
*
* @author gerry
*
*/
public class Verrechnet extends PersistentObject {
public static final String DETAIL = "Detail";
public static final String SCALE2 = "Scale2";
public static final String SCALE = "Scale";
public static final String LEISTG_CODE = "Leistg_code";
public static final String LEISTG_TXT = "Leistg_txt";
public static final String KONSULTATION = "Konsultation";
public static final String PRICE_SELLING = "VK_Preis";
public static final String SCALE_SELLING = "VK_Scale";
public static final String SCALE_TP_SELLING = "VK_TP";
public static final String COST_BUYING = "EK_Kosten";
public static final String COUNT = "Zahl";
public static final String CLASS = "Klasse";
public static final String USERID = "userID";
public static final String TABLENAME = "LEISTUNGEN";
public static final String VATSCALE = Constants.VAT_SCALE;
public static final String FLD_EXT_PRESC_ID = Constants.FLD_EXT_PRESC_ID;
// keep a list of all ch.elexis.VerrechnetAdjuster extensions
private static ArrayList<IVerrechnetAdjuster> adjusters = new ArrayList<IVerrechnetAdjuster>();
private StockService stockService = CoreHub.getStockService();
static {
addMapping(TABLENAME, KONSULTATION + "=Behandlung", LEISTG_TXT, LEISTG_CODE, CLASS, COUNT,
COST_BUYING, SCALE_TP_SELLING, SCALE_SELLING, PRICE_SELLING, SCALE, SCALE2,
"ExtInfo=" + DETAIL, USERID);
List<IConfigurationElement> adjustersConfigurations =
Extensions.getExtensions(ExtensionPointConstantsData.VERRECHNUNGSCODE_ADJUSTER);
for (IConfigurationElement elem : adjustersConfigurations) {
Object o;
try {
o = elem.createExecutableExtension("class");
if (o instanceof IVerrechnetAdjuster) {
adjusters.add((IVerrechnetAdjuster) o);
}
} catch (CoreException e) {
// just log the failed instantiation
ExHandler.handle(e);
}
}
}
public Verrechnet(final IVerrechenbar iv, final Konsultation kons, final int zahl){
TimeTool dat = new TimeTool(kons.getDatum());
Fall fall = kons.getFall();
int tp = iv.getTP(dat, fall);
double factor = iv.getFactor(dat, fall);
long preis = Math.round(tp * factor);
String[] fields = new String[] {
KONSULTATION, LEISTG_TXT, LEISTG_CODE, CLASS, COUNT, COST_BUYING, SCALE_TP_SELLING,
SCALE_SELLING, PRICE_SELLING, SCALE, SCALE2, USERID
};
String[] values = new String[] {
kons.getId(), iv.getText(), iv.getId(), iv.getClass().getName(), Integer.toString(zahl),
iv.getKosten(dat).getCentsAsString(), Integer.toString(tp), Double.toString(factor),
Long.toString(preis), "100", "100", CoreHub.actUser.getId()
};
create(null, fields, values);
if (iv instanceof Artikel) {
stockService.performSingleDisposal((Artikel) iv, 1);
}
// call the adjusters
for (IVerrechnetAdjuster adjuster : adjusters) {
adjuster.adjust(this);
}
}
/**
* Create a copy of the {@link Verrechnet} for the {@link Rechnung}.
*
* @param bill
*/
public void createCopy(Rechnung bill){
new VerrechnetCopy(this, bill);
}
public String getText(){
return checkNull(get(LEISTG_TXT));
}
public void setText(String text){
set(LEISTG_TXT, text);
}
/**
* Taxpunktwert auslesen
*
* @return
*/
public double getTPW(){
return checkZeroDouble(get(SCALE_SELLING));
}
/**
* set the primary scale factor (usually system specific or "internal" to the code system NOTE:
* This ist NOT identical to the multiplier or "Taxpunkt". The final price will be calculated as
* VK_PREIS * VK_SCALE * primaryScale * secondaryScale
*
* @param scale
* the new scale value as x.x
*/
public void setPrimaryScaleFactor(double scale){
int sca = (int) Math.round(scale * 100);
setInt(SCALE, sca);
}
/**
* get the prinary scale factor
*
* @see setPrimaryScaleFactor
* @return the primary svcale factor as double
*/
public double getPrimaryScaleFactor(){
int sca = checkZero(get(SCALE));
if (sca == 0) {
return 1.0;
}
return ((double) sca) / 100.0;
}
/**
* Set the secondary scale factor
*
* @see setPromaryScaleFactor
* @param scale
* the factor
*/
public void setSecondaryScaleFactor(double scale){
int sca = (int) Math.round(scale * 100);
setInt(SCALE2, sca);
}
/**
* Get the secondary scale factor
*
* @see setPrimaryScaleFactor
* @return the factor
*/
public double getSecondaryScaleFactor(){
int sca = checkZero(get(SCALE2));
if (sca == 0) {
return 1.0;
}
return ((double) sca) / 100.0;
}
/**
* Taxpunktpreis setzen
*
* @param tp
*/
public void setTP(double tp){
set(SCALE_TP_SELLING, Long.toString(Math.round(tp)));
}
/**
* Den effektiven Preis setzen (braucht nicht TP*Scale zu sein
*
* @deprecated use setTP and setFactor
*/
@Deprecated
public void setPreis(final Money m){
set(PRICE_SELLING, m.getCentsAsString());
}
/**
* Einkaufskosten
*/
public Money getKosten(){
// System.out.println(getText());
return new Money(checkZero(get(COST_BUYING)));
}
/**
* Den effektiv verrechneten Preis holen (braucht nicht TP*Scale zu sein
*
* @deprecated
*/
@Deprecated
public Money getEffPreis(){
return new Money(checkZero(get(PRICE_SELLING)));
/*
* double amount=checkZero(get("VK_Preis"))*checkZero(get("Scale"))/100.0; return new
* Money((int)Math.round(amount));
*/
}
/**
* Den Preis nach Anwendung sämtlicher SKalierungsfaktoren zurückgeben
*
* @return
*/
public Money getNettoPreis(){
Money brutto = getBruttoPreis();
brutto.multiply(getPrimaryScaleFactor());
brutto.multiply(getSecondaryScaleFactor());
// call the adjusters
for (IVerrechnetAdjuster adjuster : adjusters) {
adjuster.adjustGetNettoPreis(this, brutto);
}
return brutto;
}
/**
* Den Preis nach Anwendung des Taxpunktwerts (aber ohne sonstige Skalierungen) holen
*/
public Money getBruttoPreis(){
int tp = checkZero(get(SCALE_TP_SELLING));
Konsultation k = getKons();
Fall fall = k.getFall();
TimeTool date = new TimeTool(k.getDatum());
IVerrechenbar v = getVerrechenbar();
double tpw = 1.0;
if (v != null) { // Unknown tax system
tpw = v.getFactor(date, fall);
}
return new Money((int) Math.round(tpw * tp));
}
/** Den Standardpreis holen (Ist immer TP*Scale, auf ganze Rappen gerundet) */
public Money getStandardPreis(){
IVerrechenbar v = getVerrechenbar();
Konsultation k = getKons();
Fall fall = k.getFall();
TimeTool date = new TimeTool(k.getDatum());
double factor = 1.0;
int tp = 0;
if (v != null) {
factor = v.getFactor(date, fall);
tp = v.getTP(date, fall);
} else {
tp = checkZero(get(SCALE_TP_SELLING));
}
return new Money((int) Math.round(factor * tp));
}
/** Bequemlichkeits-Shortcut für Standardbetrag setzen */
public void setStandardPreis(){
IVerrechenbar v = getVerrechenbar();
Konsultation k = getKons();
Fall fall = k.getFall();
TimeTool date = new TimeTool(k.getDatum());
double factor = v.getFactor(date, fall);
int tp = v.getTP(date, fall);
long preis = Math.round(tp * factor);
set(new String[] {
SCALE_SELLING, SCALE_TP_SELLING, PRICE_SELLING
}, Double.toString(factor), Integer.toString(tp), Long.toString(preis));
}
public Konsultation getKons(){
return Konsultation.load(get(KONSULTATION));
}
/** Wie oft wurde die Leistung bei derselben Kons. verrechnet? */
public int getZahl(){
return checkZero(get(COUNT));
}
public void setZahl(final int z){
set(COUNT, Integer.toString(z));
}
public String getCode(){
IVerrechenbar verrechenbar = getVerrechenbar();
if (verrechenbar == null) {
return "?";
} else {
return verrechenbar.getCode();
}
}
/**
* Set arbitrary additional informations to a service
*
* @param key
* name of the information
* @param value
* value of the information
*/
@SuppressWarnings("unchecked")
public void setDetail(final String key, final String value){
Map ext = getMap(DETAIL);
if (value == null) {
ext.remove(key);
} else {
ext.put(key, value);
}
setMap(DETAIL, ext);
}
/**
* retrieve additional information
*
* @param key
* name of the requested information
* @return value if the information or null if no information with that name was found
*/
@SuppressWarnings("unchecked")
public String getDetail(final String key){
Map ext = getMap(DETAIL);
return (String) ext.get(key);
}
/**
* Change the count for this service or article. If it ist an Artikel, the store will be updated
* accordingly
*
* @param neuAnzahl
* new count this service is to be billed.
*/
public void changeAnzahl(int neuAnzahl){
int vorher = getZahl();
setZahl(neuAnzahl);
IVerrechenbar vv = getVerrechenbar();
if (vv instanceof Artikel) {
Artikel art = (Artikel) vv;
stockService.performSingleReturn(art, vorher);
stockService.performSingleDisposal(art, neuAnzahl);
}
}
/**
* Change the count for this service or article, considering the rules given by the resp.
* optifiers.
*
* @param neuAnzahl
* @return
*/
public IStatus changeAnzahlValidated(int neuAnzahl){
int vorher = getZahl();
if (neuAnzahl == vorher) {
return Status.OK_STATUS;
}
Konsultation kons = getKons();
if (neuAnzahl == 0) {
kons.removeLeistung(this);
return Status.OK_STATUS;
}
int difference = neuAnzahl - vorher;
if (difference > 0) {
IVerrechenbar verrechenbar = getVerrechenbar();
for (int i = 0; i < difference; i++) {
Result<IVerrechenbar> result = kons.addLeistung(verrechenbar);
if (!result.isOK()) {
String message = result.getMessages().stream().map(m -> m.getText())
.collect(Collectors.joining(", "));
return new Status(Status.ERROR, CoreHub.PLUGIN_ID, message);
}
}
} else if (difference < 0) {
changeAnzahl(neuAnzahl);
}
return Status.OK_STATUS;
}
/** Frage, ob dieses Verrechnet aus dem IVerrechenbar tmpl entstanden ist */
public boolean isInstance(final IVerrechenbar tmpl){
String[] res = new String[2];
get(new String[] {
CLASS, LEISTG_CODE
}, res);
if (tmpl.getClass().getName().equals(res[0])) {
if (tmpl.getId().equals(res[1])) {
return true;
}
}
return false;
}
/**
* return the IVerrechenbar this Verrechnet is based on
*/
public IVerrechenbar getVerrechenbar(){
String[] res = new String[2];
get(new String[] {
CLASS, LEISTG_CODE
}, res);
try {
return (IVerrechenbar) CoreHub.poFactory.createFromString(res[0] + "::" + res[1]);
} catch (Exception ex) {
log.error("Fehlerhafter Leistungscode " + getLabel());
}
return null;
}
@Override
public String getLabel(){
return checkNull(get(LEISTG_TXT));
}
@Override
protected String getTableName(){
return TABLENAME;
}
public static Verrechnet load(final String id){
return new Verrechnet(id);
}
protected Verrechnet(){}
protected Verrechnet(final String id){
super(id);
}
}