OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 149 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
144 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.erp.core.sales.pos.model;
15
 
16
import org.openconcerto.erp.core.sales.pos.model.RegisterFiles.HashMode;
17
import org.openconcerto.erp.core.sales.pos.model.RegisterLogEntry.ReceiptEntry;
18
import org.openconcerto.erp.core.sales.pos.model.RegisterLogEntry.RegisterEntry;
19
import org.openconcerto.erp.core.sales.pos.model.RegisterState.Status;
20
import org.openconcerto.utils.CollectionUtils;
21
import org.openconcerto.utils.CompareUtils;
22
 
23
import java.io.IOException;
24
import java.nio.file.Path;
25
import java.text.ParseException;
26
import java.util.ArrayList;
27
import java.util.Collections;
28
import java.util.List;
29
 
30
import org.jdom2.Document;
31
import org.jdom2.Element;
32
import org.jdom2.JDOMException;
33
 
34
/**
35
 * To parse the log file for a register.
36
 *
37
 * @author sylvain
38
 */
39
public class RegisterLog {
40
 
41
    // TODO RECEIPT_MODIFICATION
42
    static public enum EventType {
43
        REGISTER_OPENING, RECEIPT_CREATION, REGISTER_CLOSURE;
44
    }
45
 
149 ilm 46
    static private final int VERSION = 2;
47
    static private final String VERSION_ATTR_NAME = "version";
48
 
49
    static final Element createRootElement() {
50
        return new Element("registerLog").setAttribute(VERSION_ATTR_NAME, String.valueOf(VERSION));
51
    }
52
 
144 ilm 53
    private final Path logFile;
54
    private Document parsed;
149 ilm 55
    private int version;
144 ilm 56
    private RegisterEntry opening;
57
 
58
    public RegisterLog(Path logFile) {
59
        super();
60
        this.logFile = logFile;
61
    }
62
 
63
    public final Path getLogFile() {
64
        return this.logFile;
65
    }
66
 
67
    public final RegisterLog parse() throws IOException, JDOMException {
68
        final Document doc = RegisterFiles.parse(this.logFile);
69
        final List<Element> elems = doc.getRootElement().getChildren();
70
        if (elems.isEmpty())
71
            throw new IllegalStateException("Empty log");
72
        final RegisterLogEntry firstEntry;
73
        try {
74
            firstEntry = RegisterLogEntry.parseEntry(elems.get(0));
75
        } catch (ParseException e) {
76
            throw new IOException(e);
77
        }
78
        if (firstEntry.getType() != EventType.REGISTER_OPENING)
79
            throw new IllegalStateException("Do not begin with " + EventType.REGISTER_OPENING + " : " + firstEntry);
80
        this.opening = (RegisterEntry) firstEntry;
81
        this.parsed = doc;
149 ilm 82
        this.version = Integer.parseInt(doc.getRootElement().getAttributeValue(VERSION_ATTR_NAME, "1"));
144 ilm 83
        return this;
84
    }
85
 
86
    public final Document getDocument() {
87
        return this.parsed;
88
    }
89
 
149 ilm 90
    public final int getVersion() {
91
        return this.version;
92
    }
93
 
144 ilm 94
    public final RegisterLogEntry getLastEvent() throws ParseException {
95
        return this.getEvent(-1);
96
    }
97
 
98
    public final RegisterLogEntry getEvent(final int index) throws ParseException {
99
        final List<Element> elems = this.parsed.getRootElement().getChildren();
100
        return RegisterLogEntry.parseEntry(elems.get(CollectionUtils.getValidIndex(elems, index, true)));
101
    }
102
 
103
    public final List<RegisterLogEntry> getAllEvents() throws ParseException {
104
        final List<Element> elems = this.parsed.getRootElement().getChildren();
105
        final List<RegisterLogEntry> res = new ArrayList<>();
106
        for (final Element elem : elems)
107
            res.add(RegisterLogEntry.parseEntry(elem));
108
        return res;
109
    }
110
 
111
    public final String getLastReceiptHash() throws ParseException {
112
        final ReceiptEntry lastReceiptCreationEvent = this.getLastReceiptCreationEvent();
113
        if (lastReceiptCreationEvent != null)
114
            return lastReceiptCreationEvent.getFileHash();
115
        else
116
            return this.getFirstRegisterEvent().getLastReceiptHash();
117
    }
118
 
119
    public final int getRegisterID() {
120
        return this.getFirstRegisterEvent().getRegisterID();
121
    }
122
 
123
    public final RegisterEntry getFirstRegisterEvent() {
124
        return this.opening;
125
    }
126
 
127
    public final RegisterEntry getLastRegisterEvent() throws ParseException {
128
        final RegisterLogEntry lastEvent = this.getLastEvent();
129
        if (lastEvent.getType() == EventType.REGISTER_CLOSURE)
130
            return (RegisterEntry) lastEvent;
131
        else
132
            return this.opening;
133
    }
134
 
135
    public final RegisterState getRegisterState() throws ParseException {
136
        final RegisterEntry lastRegisterEvent = this.getLastRegisterEvent();
137
        return new RegisterState(lastRegisterEvent.getType() == EventType.REGISTER_OPENING ? Status.OPEN : Status.CLOSED, lastRegisterEvent.getDate());
138
    }
139
 
140
    public final List<ReceiptEntry> getReceiptEvents() throws ParseException {
141
        final List<Element> elems = this.parsed.getRootElement().getChildren();
142
        final int size = elems.size();
143
        if (size <= 1)
144
            return Collections.emptyList();
145
        final List<ReceiptEntry> res = new ArrayList<>();
146
        for (final Element elem : elems.subList(1, size)) {
147
            final RegisterLogEntry parsedEntry = RegisterLogEntry.parseEntry(elem);
148
            if (parsedEntry instanceof ReceiptEntry) {
149
                res.add((ReceiptEntry) parsedEntry);
150
            } else if (parsedEntry.getType() == EventType.REGISTER_CLOSURE) {
151
                if (res.size() != size - 2)
152
                    throw new IllegalStateException("Closure not at the end : " + elems);
153
            } else {
154
                throw new IllegalStateException("Unexpected entry : " + parsedEntry);
155
            }
156
        }
157
        return res;
158
    }
159
 
160
    /**
161
     * Parse all receipts logged in this. This also validates that data recorded in this log matches
162
     * the parsed receipts' files.
163
     *
164
     * @return the list of current receipts in this.
165
     * @throws ParseException if an entry couldn't be parsed.
174 ilm 166
     * @throws IOException if the receipts couldn't be parsed.
144 ilm 167
     */
174 ilm 168
    public final List<Ticket> parseReceipts() throws ParseException, IOException {
144 ilm 169
        final List<Ticket> receipts = new ArrayList<>();
170
        final Path toUse = this.getLogFile().getParent();
171
        final List<ReceiptEntry> receiptEvents = this.getReceiptEvents();
172
        String lastHash = this.getFirstRegisterEvent().getLastReceiptHash();
173
        int index = 1;
174
        for (final ReceiptEntry receiptEntry : receiptEvents) {
175
            final Path receiptFile = toUse.resolve(receiptEntry.getCode().getFileName());
174 ilm 176
            final Ticket receipt;
177
            try {
178
                receipt = Ticket.parseFile(receiptFile.toFile(), HashMode.equalTo(receiptEntry.getFileHash()));
179
            } catch (Exception e) {
180
                throw new IOException("Couldn't parse file of " + receiptEntry + " : " + receiptFile, e);
181
            }
182
            assert receipt != null;
144 ilm 183
            if (!receipt.getCode().equals(receiptEntry.getCodeString()))
184
                throw new IllegalStateException("Code mismatch");
185
            if (!CompareUtils.equals(lastHash, receipt.getPreviousHash()))
186
                throw new IllegalStateException("Previous hash mismatch for " + receiptEntry.getCodeString());
187
            lastHash = receiptEntry.getFileHash();
188
            if (receipt.getNumber() != index)
189
                throw new IllegalStateException("Invalid index, expected " + index + " got " + receipt.getNumber());
190
            index++;
191
            if (receipt.getCreationDate().compareTo(receiptEntry.getDate()) != 0)
192
                throw new IllegalStateException("Date mismatch for " + receiptEntry.getCodeString() + " logged " + receiptEntry.getDate() + " but receipt " + receipt.getCreationDate());
193
            receipts.add(receipt);
194
        }
195
        return receipts;
196
    }
197
 
198
    public final ReceiptEntry getLastReceiptCreationEvent() throws ParseException {
199
        for (int i = -1; i >= -2; i--) {
200
            final RegisterLogEntry event = this.getEvent(i);
201
            if (event.getType() == EventType.REGISTER_OPENING) {
202
                return null;
203
            } else if (event.getType() == EventType.RECEIPT_CREATION) {
204
                return (ReceiptEntry) event;
205
            }
206
        }
207
        throw new IllegalStateException("2 " + EventType.REGISTER_CLOSURE);
208
    }
209
 
210
    @Override
211
    public String toString() {
212
        return this.getClass().getSimpleName() + " " + this.getLogFile() + " opened " + this.getFirstRegisterEvent().getDate();
213
    }
214
}