OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 150 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package org.openconcerto.modules.operation;

import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.SwingUtilities;

import org.jopencalendar.model.Flag;
import org.jopencalendar.model.JCalendarItem;
import org.jopencalendar.ui.MultipleDayView;
import org.jopencalendar.ui.WeekView;
import org.openconcerto.erp.core.humanresources.payroll.element.SalarieSQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.users.User;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.ui.DisplayabilityListener;
import org.openconcerto.ui.list.CheckListItem;

import net.jcip.annotations.GuardedBy;

public class UserOperationListModel extends CheckListModel<User> {

    protected static String formatDuration(int durationMinute) {
        int h = durationMinute / 60;
        int m = durationMinute % 60;
        if (m != 0) {
            String mS = String.valueOf(m);
            if (m < 10) {
                mS = "0" + mS;
            }
            return h + ":" + mS;
        }
        return String.valueOf(h);
    }

    private static final class UsersListener implements PropertyChangeListener, SQLTableModifiedListener {

        private final UserOperationListModel model;

        protected UsersListener(UserOperationListModel model) {
            super();
            this.model = model;
        }

        @Override
        public void tableModified(SQLTableEvent evt) {
            this.model.refreshUsers();
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            this.model.refreshUsers();
        }
    }

    private final MultipleDayView view;
    private final UserManager userMngr;
    private final SalarieSQLElement salarieElem;

    // all users to display and their the weekly work time, if null, not a Salarie
    @GuardedBy("this")
    private Map<User, Integer> usersAndWeeklyMinutes;
    private final UsersListener usersL = new UsersListener(this);
    // durations by user ID
    @GuardedBy("this")
    private Map<Integer, Long> allDurations;
    @GuardedBy("this")
    private Map<Integer, Long> lockedDurations;
    private final PropertyChangeListener viewL = new PropertyChangeListener() {
        @SuppressWarnings("unchecked")
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            assert SwingUtilities.isEventDispatchThread();
            setDurations((List<List<JCalendarItem>>) evt.getNewValue());
            loadContent();
        }
    };

    public UserOperationListModel(final UserManager userMngr, final SQLElementDirectory dir, final MultipleDayView multipleDayView) {
        super(User.class);
        this.view = multipleDayView;
        this.userMngr = userMngr;
        this.salarieElem = dir.getElement(SalarieSQLElement.class);
        this.view.addHierarchyListener(new DisplayabilityListener() {
            @Override
            protected void displayabilityChanged(Component c) {
                setRunning(c.isDisplayable());
            }
        });
        // initial value
        setRunning(this.view.isDisplayable());
    }

    public final void setRunning(final boolean b) {
        if (b) {
            this.userMngr.addUsersListener(this.usersL);
            for (final SQLTable salT : this.salarieElem.createGraph(VirtualFields.PRIMARY_KEY).getGraphTables()) {
                salT.addTableModifiedListener(this.usersL);
            }
            this.view.addPropertyChangeListener(WeekView.CALENDARD_ITEMS_PROPERTY, this.viewL);
            // initial value
            refreshUsers();
        } else {
            this.userMngr.removeUsersListener(this.usersL);
            for (final SQLTable salT : this.salarieElem.createGraph(VirtualFields.PRIMARY_KEY).getGraphTables()) {
                salT.removeTableModifiedListener(this.usersL);
            }
            this.view.removePropertyChangeListener(WeekView.CALENDARD_ITEMS_PROPERTY, this.viewL);
        }
    }

    private void refreshUsers() {
        synchronized (this) {
            this.usersAndWeeklyMinutes = null;
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                loadContent();
            }
        });
    }

    private Map<User, Integer> setUsers() {
        synchronized (this) {
            if (this.usersAndWeeklyMinutes != null) {
                return this.usersAndWeeklyMinutes;
            }
        }
        final Map<User, Integer> uInfo = new LinkedHashMap<>();
        final SQLRowValues v = new SQLRowValues(this.salarieElem.getTable());
        v.putNulls("NOM", "PRENOM");
        v.putRowValues("ID_INFOS_SALARIE_PAYE").putNulls("DUREE_HEBDO");
        final List<SQLRowValues> rows = SQLRowValuesListFetcher.create(v).fetch();
        final List<User> users = new ArrayList<>(this.userMngr.getAllActiveUsers());
        // Sort by full name
        Collections.sort(users, new UserComparator());
        final int size = users.size();
        for (int i = 0; i < size; i++) {
            final User u = users.get(i);
            final String name = u.getName().trim();
            final String firstName = u.getFirstName().trim();
            Integer minutes = null;
            for (SQLRowValues row : rows) {
                // Matching Utilisateur <-> Salarié
                // Nom et prénom identique
                final String sName = row.getString("NOM").trim();
                final String sFirstName = row.getString("PRENOM").trim();
                if (sName.equalsIgnoreCase(name) && sFirstName.equalsIgnoreCase(firstName)) {
                    minutes = (int) row.getForeign("ID_INFOS_SALARIE_PAYE").getFloat("DUREE_HEBDO") * 60;
                    break;
                }
            }
            uInfo.put(u, minutes);
        }
        synchronized (this) {
            this.usersAndWeeklyMinutes = Collections.unmodifiableMap(uInfo);
        }
        return uInfo;
    }

    private void setDurations(final List<List<JCalendarItem>> viewItems) {
        final Map<Integer, Long> all = OperationCalendarPanel.getDurations(viewItems, null, ModuleOperation.FREE_TIME_FLAG);
        final Map<Integer, Long> locked = OperationCalendarPanel.getDurations(viewItems, Flag.getFlag("locked"), ModuleOperation.FREE_TIME_FLAG);
        synchronized (this) {
            this.allDurations = Collections.unmodifiableMap(all);
            this.lockedDurations = Collections.unmodifiableMap(locked);
        }
    }

    // static to make sure that the returned object doesn't depend on any instance attribute
    static protected CheckListItem createItem(final User u, final String label) {
        final CheckListItem item = new CheckListItem(u, true) {

            @Override
            public Object getKey() {
                return u.getId();
            }

            @Override
            public String toString() {
                return label;
            }
        };
        item.setColor(UserColor.getInstance().getColor(u.getId()));
        return item;
    }

    static private final int getDuration(final Map<Integer, Long> m, final Integer key) {
        final Long res = m == null ? null : m.get(key);
        return res == null ? 0 : res.intValue();
    }

    @Override
    public List<CheckListItem> loadItems() {
        final Map<User, Integer> usersAndWeeklyMinutes = setUsers();
        final Map<Integer, Long> all, locked;
        synchronized (this) {
            all = this.allDurations;
            locked = this.lockedDurations;
        }
        final List<CheckListItem> res = new ArrayList<>(usersAndWeeklyMinutes.size());
        for (final Entry<User, Integer> e : usersAndWeeklyMinutes.entrySet()) {
            final User u = e.getKey();
            final Integer weeklyMinutes = e.getValue();
            final String suffix;
            if (weeklyMinutes == null) {
                // not a SALARIE
                suffix = "";
            } else {
                // Durée verrouillée
                final int d2 = getDuration(locked, u.getId());
                // Durée planifiée
                final int d = getDuration(all, u.getId());
                suffix = " [" + formatDuration(d2) + " / " + formatDuration(d) + " / " + formatDuration(weeklyMinutes) + "]";
            }
            res.add(createItem(u, (u.getFullName() + suffix).trim()));
        }
        return res;
    }
}