OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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

package org.openconcerto.modules.operation;

import java.awt.FileDialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jopencalendar.model.JCalendarItem;
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.generationDoc.TemplateManager;
import org.openconcerto.openoffice.OOUtils;
import org.openconcerto.openoffice.spreadsheet.Sheet;
import org.openconcerto.openoffice.spreadsheet.SpreadSheet;
import org.openconcerto.sql.PropsConfiguration;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.FieldMapper;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.sqlobject.SQLTextCombo;
import org.openconcerto.sql.sqlobject.SQLTextCombo.ITextComboCacheSQL;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.JDate;
import org.openconcerto.ui.date.DateRangePlannerPanel;
import org.openconcerto.utils.CollectionMap2.Mode;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.DecimalUtils;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.StreamUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.TimeUtils;
import org.openconcerto.xml.JDOM2Utils;

import net.jcip.annotations.GuardedBy;

public class OperationExportPanel extends JPanel {

    @GuardedBy("EDT")
    static private final DateFormat DF = new SimpleDateFormat("yyyyMMdd");

    final JCheckBox lockedCheckBox = new JCheckBox("verrouillées uniquement");
    final JButton bPrint = new JButton("Exporter");

    public OperationExportPanel(final OperationCalendarManager manager, final List<SQLRowValues> rowsSite) {
        lockedCheckBox.setSelected(true);
        //
        this.setLayout(new GridBagLayout());
        final GridBagConstraints c = new DefaultGridBagConstraints();
        final JLabel l = new JLabel("Date de début", SwingConstants.RIGHT);
        this.add(l, c);
        c.gridx++;
        final JDate d1 = new JDate(false, true);
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        d1.setDate(cal.getTime());
        c.weightx = 1;
        this.add(d1, c);
        c.gridx = 0;
        c.gridy++;
        c.weightx = 0;
        final JLabel l2 = new JLabel("Date de fin", SwingConstants.RIGHT);

        this.add(l2, c);
        c.gridx++;
        final JDate d2 = new JDate(false, true);
        cal.add(Calendar.MONTH, 1);
        cal.add(Calendar.SECOND, -1);
        d2.setDate(cal.getTime());
        c.weightx = 1;
        this.add(d2, c);

        final OperationSQLElement operationElem = manager.getDirectory().getElement(OperationSQLElement.class);
        final OperationSQLComponent operationSQLComp = new OperationSQLComponent(operationElem);
        final String statusID = "operation.status";
        c.gridy++;
        c.gridx = 0;
        c.gridwidth = 1;
        this.add(operationSQLComp.getLabel(statusID), c);
        c.gridx++;
        final SQLTextCombo statusCombo = (SQLTextCombo) operationSQLComp.createEditor(statusID);
        final FieldMapper fieldMapper = PropsConfiguration.getInstance().getFieldMapper();
        statusCombo.initCache(new ITextComboCacheSQL(fieldMapper.getSQLFieldForItem(statusID)));
        this.add(statusCombo, c);

        //
        c.gridwidth = 2;
        c.gridx = 0;
        c.gridy++;
        final JRadioButton radio1 = new JRadioButton("interventions");
        radio1.setSelected(true);
        this.add(radio1, c);
        final JRadioButton radio2 = new JRadioButton("planifications");
        c.gridy++;
        this.add(radio2, c);
        final ButtonGroup g = new ButtonGroup();
        g.add(radio1);
        g.add(radio2);

        //
        final JPanel p = new JPanel();
        p.setLayout(new FlowLayout(FlowLayout.RIGHT));
        p.add(lockedCheckBox);
        p.add(bPrint);
        c.gridwidth = 2;
        c.gridx = 0;
        c.gridy++;
        c.fill = GridBagConstraints.NONE;
        c.anchor = GridBagConstraints.SOUTHEAST;
        this.add(p, c);
        //

        bPrint.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (d1.getDate().after(d2.getDate())) {
                    return;
                }
                final String statusVal = statusCombo.getValue();
                final List<String> states = StringUtils.isEmpty(statusVal, true) ? null : Collections.singletonList(statusVal);
                final List<JCalendarItem> items = manager.getItemIn(d1.getDate(), d2.getDate(), null, states);
                final List<JCalendarItemDB> itemsToExport = new ArrayList<JCalendarItemDB>(items.size());
                if (lockedCheckBox.isSelected()) {
                    for (JCalendarItem jCalendarItem : items) {
                        JCalendarItemDB i = (JCalendarItemDB) jCalendarItem;
                        if (i.getFlagsString().contains("locked")) {
                            itemsToExport.add(i);
                        }
                    }
                } else {
                    for (JCalendarItem jCalendarItem : items) {
                        JCalendarItemDB i = (JCalendarItemDB) jCalendarItem;
                        itemsToExport.add(i);
                    }
                }
                if (rowsSite != null && !rowsSite.isEmpty()) {
                    final Set<String> allowedSites = new HashSet<String>();
                    for (SQLRowValues r : rowsSite) {
                        String siteName = r.getString("NAME");
                        allowedSites.add(siteName);
                    }
                    final List<JCalendarItemDB> filtered = new ArrayList<JCalendarItemDB>(itemsToExport.size());
                    for (JCalendarItemDB i : itemsToExport) {
                        if (allowedSites.contains(i.getSiteName())) {
                            filtered.add(i);
                        }
                    }
                    itemsToExport.clear();
                    itemsToExport.addAll(filtered);
                }

                if (itemsToExport.isEmpty()) {
                    JOptionPane.showMessageDialog(OperationExportPanel.this, "Aucune intervention trouvée.\nMerci de vérifier la période et le verrouillage des interventions.");
                    return;
                }

                Collections.sort(itemsToExport, new Comparator<JCalendarItem>() {

                    @Override
                    public int compare(JCalendarItem o1, JCalendarItem o2) {
                        if (o1.getUserId().equals(o2.getUserId())) {
                            return o1.getDtStart().getTime().compareTo(o2.getDtStart().getTime());
                        }
                        return (int) (((Number) o1.getUserId()).longValue() - ((Number) o2.getUserId()).longValue());
                    }
                });
                final String dates = " " + DF.format(d1.getDate()) + '-' + DF.format(d2.getDate());
                if (radio1.isSelected()) {
                    export(itemsToExport, "export " + radio1.getText() + dates);
                } else {
                    exportPlan(itemsToExport, "export " + radio2.getText() + dates);
                }
                closeFrame();

            }
        });
    }

    static private final class Planner implements Comparable<Planner> {

        static private final BigDecimal MS_PER_HOUR = BigDecimal.valueOf(1000 * 3600);

        private final String uid;
        private final String xml;
        private Date rangeStart;
        private Date startTime;
        private BigDecimal hours;

        protected Planner(String uid, String xml) {
            super();
            this.uid = uid;
            this.xml = xml;
        }

        public final String getUID() {
            return this.uid;
        }

        public final String getDescription() {
            return DateRangePlannerPanel.getDescriptionFromXML(this.xml, false);
        }

        public final Date getRangeStart() {
            if (this.rangeStart == null) {
                parse();
            }
            return this.rangeStart;
        }

        protected void parse() {
            try {
                final Document doc = JDOM2Utils.parseStringDocument(this.xml);
                this.rangeStart = new Date(Long.valueOf(doc.getRootElement().getChild("range").getAttributeValue("start")));

                final Element scheduleElem = doc.getRootElement().getChild("schedule");
                this.startTime = new Date(Long.valueOf(scheduleElem.getAttributeValue("start")));
                final long endTime = Long.valueOf(scheduleElem.getAttributeValue("end"));
                this.hours = DecimalUtils.round(BigDecimal.valueOf(endTime - this.startTime.getTime()).divide(MS_PER_HOUR, DecimalUtils.HIGH_PRECISION), 5);
            } catch (Exception e) {
                throw new IllegalStateException("couldn't get start for " + this.xml, e);
            }
        }

        public final Date getStartTime() {
            if (this.startTime == null) {
                parse();
            }
            return this.startTime;
        }

        public final BigDecimal getHours() {
            if (this.hours == null) {
                parse();
            }
            return this.hours;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + this.uid.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Planner other = (Planner) obj;
            return this.uid.equals(other.uid);
        }

        @Override
        public int compareTo(Planner o) {
            if (this.equals(o))
                return 0;
            final int startComparison = this.getRangeStart().compareTo(o.getRangeStart());
            if (startComparison != 0)
                return startComparison;
            return this.getUID().compareTo(o.getUID());

        }
    }

    protected void exportPlan(List<JCalendarItemDB> itemsToExport, final String name) {

        final List<Long> ids = ModuleOperation.getOperationIdsFrom(new HashSet<JCalendarItemDB>(itemsToExport));

        final DBRoot root = ComptaPropsConfiguration.getInstanceCompta().getRootSociete();
        final SQLTable table = root.getTable(ModuleOperation.TABLE_OPERATION);
        final SQLRowValues valOperation = new SQLRowValues(table);

        final SQLRowValues valSite = new SQLRowValues(root.getTable(ModuleOperation.TABLE_SITE));
        valSite.putNulls("NAME", "COMMENT");

        valOperation.putRowValues("ID_USER_COMMON").putNulls("NOM", "PRENOM");
        // valOperation.put("ID_CALENDAR_ITEM_GROUP", valsCalendarItemsGroup);
        valOperation.put("ID_SITE", valSite);
        valOperation.putNulls("STATUS", "DESCRIPTION", "TYPE", "PLANNER_XML", "PLANNER_UID");

        final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(valOperation);
        fetcher.setFullOnly(true);

        final List<SQLRowValues> itemsFetched = fetcher.fetch(new Where(table.getKey(), ids).and(Where.createRaw("length(" + table.getField("PLANNER_UID").getFieldRef() + ")>2")));
        final ListMap<Planner, SQLRowValues> operationsByPlanner = new ListMap<>(new TreeMap<Planner, List<SQLRowValues>>(), Mode.NULL_FORBIDDEN);
        for (SQLRowValues d : itemsFetched) {
            operationsByPlanner.add(new Planner(d.getString("PLANNER_UID"), d.getString("PLANNER_XML")), d);
        }
        final List<Entry<Planner, List<SQLRowValues>>> items = new ArrayList<>(operationsByPlanner.entrySet());
        final DatatypeFactory dataTypeFactory;
        try {
            dataTypeFactory = DatatypeFactory.newInstance();
        } catch (DatatypeConfigurationException e) {
            throw new IllegalStateException(e);
        }

        TableModel model = new AbstractTableModel() {

            @Override
            public int getRowCount() {
                return items.size();
            }

            @Override
            public int getColumnCount() {
                return 7;
            }

            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                final Entry<Planner, List<SQLRowValues>> rows = items.get(rowIndex);
                final Planner planner = rows.getKey();
                // for now, only the "Employé" column check all operations for a planner, the other
                // columns just take the first one.
                final SQLRowValues i = rows.getValue().get(0);
                switch (columnIndex) {
                case 0:
                    return dataTypeFactory.newDurationDayTime(planner.getStartTime().getTime());

                case 1:
                    return planner.getHours();

                case 2:
                    // Plannif
                    return planner.getDescription();

                case 3:
                    // Employé
                    final ListMap<Integer, SQLRowValues> operationsByUserID = new ListMap<>();
                    for (final SQLRowValues r : rows.getValue()) {
                        operationsByUserID.add(r.getForeignID("ID_USER_COMMON"), r);
                    }
                    final List<Entry<Integer, List<SQLRowValues>>> entries = new ArrayList<>(operationsByUserID.entrySet());
                    // the users with the most operations first
                    Collections.sort(entries, new Comparator<Entry<Integer, List<SQLRowValues>>>() {
                        @Override
                        public int compare(Entry<Integer, List<SQLRowValues>> o1, Entry<Integer, List<SQLRowValues>> o2) {
                            final int sizeComparison = CompareUtils.compareInt(o1.getValue().size(), o2.getValue().size());
                            if (sizeComparison != 0)
                                return -sizeComparison;
                            return o1.getKey().compareTo(o2.getKey());
                        }
                    });
                    final ListIterator<Entry<Integer, List<SQLRowValues>>> listIter = entries.listIterator();
                    final StringBuilder sb = new StringBuilder(128);
                    while (listIter.hasNext()) {
                        final Entry<Integer, List<SQLRowValues>> element = listIter.next();
                        final SQLRowValues operationR = element.getValue().get(0);
                        final SQLRowAccessor userR = operationR.getForeign("ID_USER_COMMON");
                        sb.append(userR.getString("PRENOM") + " " + userR.getString("NOM"));
                        if (listIter.previousIndex() > 0) {
                            sb.append(" (");
                            sb.append(element.getValue().size());
                            sb.append(")");
                        }
                        if (listIter.hasNext()) {
                            sb.append(", ");
                        }
                    }
                    return sb.toString();
                case 4:
                    // Nature
                    final String type = i.getString("TYPE");
                    return type;
                case 5:
                    // Chantier
                    final String siteName = i.getForeign("ID_SITE").getString("NAME");
                    return siteName;
                case 6:
                    // Description
                    final String desc = i.getString("DESCRIPTION");
                    return desc;
                default:
                    break;
                }
                return "?";
            }

        };

        // Save the data to an ODS file and open it.
        final String templateId = ModuleOperation.OPERATIONS_REPORT_TEMPLATE2_ID;
        saveAsODS(model, templateId, name);
    }

    protected void export(final List<JCalendarItemDB> items, final String name) {

        TableModel model = new AbstractTableModel() {

            @Override
            public int getRowCount() {
                return items.size();
            }

            @Override
            public int getColumnCount() {
                return 7;
            }

            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                JCalendarItemDB i = items.get(rowIndex);
                switch (columnIndex) {
                case 0:
                    // Date
                    Date start = TimeUtils.clearTime((Calendar) i.getDtStart().clone()).getTime();
                    return start;
                case 1:
                    // Heure
                    Calendar h = i.getDtStart();
                    return h;
                case 2:
                    // Durée
                    long m = (i.getDtEnd().getTimeInMillis() - i.getDtStart().getTimeInMillis()) / (1000 * 60);
                    return m;
                case 3:
                    // Employé
                    final SQLRowValues user = i.getUserRow();
                    return user.getString("PRENOM") + " " + user.getString("NOM");
                case 4:
                    // Nature
                    final String type = i.getType();
                    return type;
                case 5:
                    // Chantier
                    final String siteName = i.getSiteName();
                    return siteName;
                case 6:
                    // Description
                    final String desc = i.getDescription();
                    return desc;
                default:
                    break;
                }
                return "?";
            }

        };

        // Save the data to an ODS file and open it.
        final String templateId = ModuleOperation.OPERATIONS_REPORT_TEMPLATE_ID;
        saveAsODS(model, templateId, name);

    }

    public void saveAsODS(TableModel model, final String templateId, final String name) {
        try {
            final InputStream inStream = TemplateManager.getInstance().getTemplate(templateId);
            final File templateFile = File.createTempFile(templateId, ".ods");
            if (inStream == null) {
                JOptionPane.showMessageDialog(this, "Modèle introuvable");
                return;
            }
            StreamUtils.copy(inStream, templateFile);
            inStream.close();
            final Sheet sheet = SpreadSheet.createFromFile(templateFile).getSheet(0);
            final int rowCount = model.getRowCount();
            sheet.ensureRowCount(rowCount + 1);
            final int columnCount = model.getColumnCount();
            for (int x = 0; x < columnCount; x++) {
                for (int y = 0; y < rowCount; y++) {
                    sheet.setValueAt(model.getValueAt(y, x), x, y + 1);
                }
            }

            final FileDialog d = new FileDialog((Frame) SwingUtilities.getWindowAncestor(this), "Exporter sous...", FileDialog.SAVE);
            d.setFile(name + ".ods");
            d.setVisible(true);
            String fileName = d.getFile();
            if (fileName != null) {
                fileName = fileName.trim();
                if (!fileName.toLowerCase().endsWith(".ods")) {
                    fileName += ".ods";
                }
                File outputFile = new File(d.getDirectory(), fileName);
                final File saveAs = sheet.getSpreadSheet().saveAs(outputFile);
                OOUtils.open(saveAs);
            } else {
                JOptionPane.showMessageDialog(this, "Fichier non spécifié");
            }
        } catch (Exception e) {
            ExceptionHandler.handle("Export error", e);
        }
    }

    protected void closeFrame() {
        SwingUtilities.getWindowAncestor(this).dispose();
    }

}