OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 144 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.erp.core.sales.pos.model;

import org.openconcerto.erp.core.sales.pos.POSConfiguration;
import org.openconcerto.erp.core.sales.pos.model.RegisterState.Status;
import org.openconcerto.erp.utils.TM;
import org.openconcerto.sql.PropsConfiguration;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.ui.SwingThreadUtils;
import org.openconcerto.utils.CollectionUtils;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.logging.Level;

import javax.swing.JOptionPane;

import net.jcip.annotations.Immutable;

/**
 * Encapsulate all the data about a register in the DB at a point in time.
 * 
 * @author sylvain
 */
@Immutable
public final class DBState {

    private static final Status getStatus(final SQLRowAccessor entry) {
        final Status res = Status.valueOf(entry.getString("EVT"));
        if (res != Status.OPEN && res != Status.CLOSED)
            throw new IllegalStateException("Invalid status : " + res);
        return res;
    }

    // used not for requests but for tables and paths
    private final RegisterDB registerDB;
    private final SQLRowValues registerVals;
    private final SQLRowValues lastReceiptVals;
    private final RegisterState registerState;
    // first is the latest
    private final List<SQLRowValues> lastEntries;

    protected DBState(final RegisterDB registerDB, final SQLRowValues registerR) {
        super();
        this.registerDB = registerDB;
        this.registerVals = registerR.toImmutable();
        if (this.getPosID() != registerDB.getPosID())
            throw new IllegalArgumentException("Not same register");
        this.lastReceiptVals = CollectionUtils.getSole(this.registerVals.getReferentRows(this.registerDB.getReceiptElement().getTable().getField("ID_CAISSE")));
        this.lastEntries = Collections.unmodifiableList(new ArrayList<>(this.registerVals.getReferentRows(this.registerDB.getLogElement().getTable().getField("ID_CAISSE"))));

        final SQLRowAccessor lastEntry = getLastEntry();
        if (lastEntry == null) {
            this.registerState = new RegisterState(Status.CLOSED, null);
        } else {
            if (lastEntry.getForeignID("ID_CAISSE") != this.getPosID())
                throw new IllegalStateException("Incoherent register IDs");
            final Status status = getStatus(lastEntry);
            this.registerState = new RegisterState(status, lastEntry.getDate("DATE").getTime());
        }
    }

    public final int getPosID() {
        return this.registerVals.getID();
    }

    public final RegisterState getRegisterState() {
        return this.registerState;
    }

    public SQLRowValues getRegisterRow() {
        return this.registerVals;
    }

    public final SQLRowValues getLastClosure() {
        return this.registerVals.followPath(this.registerDB.getRegisterToLastClosureEntry().minusLast());
    }

    public final SQLRowValues getLastClosureEntry() {
        return this.registerVals.followPath(this.registerDB.getRegisterToLastClosureEntry());
    }

    public final SQLRowValues getLastOpeningEntry() {
        final List<SQLRowValues> entries = this.getLastEntries();
        if (entries.isEmpty()) {
            // never opened
            assert !this.getRegisterState().hasDate();
            return null;
        } else if (getStatus(entries.get(0)).equals(Status.OPEN)) {
            return entries.get(0);
        } else if (entries.size() > 1 && getStatus(entries.get(1)).equals(Status.OPEN)) {
            return entries.get(1);
        } else {
            throw new IllegalStateException("Two consecutive non-open entries : " + entries);
        }
    }

    public final SQLRowValues getLastEntry() {
        return CollectionUtils.getFirst(this.getLastEntries());
    }

    public final List<SQLRowValues> getLastEntries() {
        return this.lastEntries;
    }

    public final SQLRowValues getLastReceiptRow() {
        return this.lastReceiptVals;
    }

    public final ReceiptCode getLastReceiptCode() throws ParseException {
        final SQLRowValues lastReceiptRow = this.getLastReceiptRow();
        return lastReceiptRow == null ? null : new ReceiptCode(lastReceiptRow.getString("NUMERO"));
    }

    public final SQLRowValues fillHostValues(final SQLRowValues logVals) {
        logVals.put("HOST_NAME", PropsConfiguration.getHostname());
        logVals.put("HOST_USER", System.getProperty("user.name"));
        return logVals;
    }

    // initial values for entries created before this version
    private static final Map<String, ?> NULL_VALUES = CollectionUtils.createMap("HOST_NAME", null, "HOST_USER", null);

    /**
     * Check if the register installation has moved and ask the user about it if it did. This can be
     * called from outside the EDT.
     * 
     * @return <code>true</code> if the user wants to quit.
     * @throws InterruptedException if this thread was interrupted while waiting on the EDT.
     * @throws ExecutionException if there was an error while asking the user.
     */
    public final boolean checkIfMoved() throws InterruptedException, ExecutionException {
        final SQLRowValues lastEntry = this.getLastEntry();
        if (lastEntry == null) {
            return false;
        }

        final Map<String, Object> currentValues = fillHostValues(new SQLRowValues(lastEntry.getTable())).getAbsolutelyAll();
        final Map<String, Object> dbValues = lastEntry.getValues(currentValues.keySet());
        if (dbValues.equals(currentValues) || NULL_VALUES.equals(dbValues)) {
            return false;
        } else {
            final String message = TM.tr("register.moved", getPosID());
            final String[] options = new String[] { TM.tr("register.moved.ignore"), TM.tr("register.moved.quit") };
            final FutureTask<Boolean> askUserCallable = new FutureTask<>(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    // quit by default
                    final int ans = JOptionPane.showOptionDialog(null, message, TM.tr("register.moved.title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[1]);
                    // CLOSED_OPTION also means quit, only clicking "ignore" means don't quit
                    return ans != 0;
                }
            });
            SwingThreadUtils.invoke(askUserCallable);
            final boolean quit = askUserCallable.get();
            if (!quit)
                POSConfiguration.getLogger().log(Level.WARNING, "User choose to ignore changed host,\nDB values : {0}\ncurrent values : {1}", new Object[] { dbValues, currentValues });
            return quit;
        }
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + " for register " + this.getPosID() + " in state " + this.getRegisterState();
    }
}