From 81245438d8b4e1ccdcfd356631b6ab8d2d059df6 Mon Sep 17 00:00:00 2001 From: j-dimension Date: Tue, 3 Dec 2024 15:15:10 +0100 Subject: [PATCH] reworked changing timesheet entries after their assignment to an invoice. close #2472 --- .../client/editors/files/InvoiceDialog.form | 49 ++++- .../client/editors/files/InvoiceDialog.java | 126 ++++++++--- .../editors/files/TimesheetBillingDialog.form | 48 ++++- .../editors/files/TimesheetBillingDialog.java | 76 ++++++- .../client/editors/files/TimesheetDialog.java | 21 +- .../files/TimesheetPositionEntryPanel.form | 6 +- .../files/TimesheetPositionEntryPanel.java | 197 ++++++++++-------- .../persistence/TimesheetPosition.java | 12 ++ 8 files changed, 396 insertions(+), 139 deletions(-) diff --git a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.form b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.form index c7f2572c4..c83dc7e51 100644 --- a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.form +++ b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.form @@ -1026,8 +1026,20 @@ + + + + + + + + + + + + @@ -1041,13 +1053,7 @@ - - - - - - - + @@ -1085,6 +1091,13 @@ + + + + + + + @@ -1220,6 +1233,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.java b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.java index 548edd778..4ae406413 100644 --- a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.java +++ b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/InvoiceDialog.java @@ -774,6 +774,7 @@ public InvoiceDialog(ArchiveFilePanel caseView, ArchiveFileBean caseDto, java.aw this.caseDto = caseDto; this.caseView = caseView; initComponents(); + this.enableTimesheetButton(false); ComponentUtils.restoreDialogSize(this); ComponentUtils.decorateSplitPane(splitMain); ComponentUtils.restoreSplitPane(splitMain, InvoiceDialog.class, "splitMain"); @@ -1117,6 +1118,9 @@ public final void setEntry(Invoice invoice) { } + List timesForInvoice = locator.lookupArchiveFileServiceRemote().getTimesheetPositionsForInvoice(invoice.getId()); + this.enableTimesheetButton(timesForInvoice != null && !timesForInvoice.isEmpty()); + } catch (Exception ex) { log.error("Error determining invoice positions", ex); JOptionPane.showMessageDialog(this, "Fehler beim Laden der Rechnungspositionen: " + ex.getMessage(), com.jdimension.jlawyer.client.utils.DesktopUtils.POPUP_TITLE_ERROR, JOptionPane.ERROR_MESSAGE); @@ -1231,6 +1235,9 @@ private void initComponents() { jLabel19 = new javax.swing.JLabel(); lblInvoiceDocumentChanged = new javax.swing.JLabel(); lblInvoiceDocumentCreated = new javax.swing.JLabel(); + lblTimesheet = new javax.swing.JLabel(); + cmdCreateTimesheetDocument = new javax.swing.JButton(); + jSeparator1 = new javax.swing.JSeparator(); jPanel3 = new javax.swing.JPanel(); jScrollPane4 = new javax.swing.JScrollPane(); tblPayments = new javax.swing.JTable(); @@ -1559,7 +1566,7 @@ public void mousePressed(java.awt.event.MouseEvent evt) { .addComponent(cmdDateDue)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(cmbCurrency, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(46, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jTabbedPane1.addTab("Kopf", jPanel4); @@ -1810,6 +1817,18 @@ public void mouseExited(java.awt.event.MouseEvent evt) { lblInvoiceDocumentCreated.setFont(lblInvoiceDocumentCreated.getFont()); lblInvoiceDocumentCreated.setText("jLabel21"); + lblTimesheet.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons16/material/baseline_timer_black_48dp.png"))); // NOI18N + lblTimesheet.setText("Leistungsnachweis"); + + cmdCreateTimesheetDocument.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/editcopy.png"))); // NOI18N + cmdCreateTimesheetDocument.setText("Erstellen"); + cmdCreateTimesheetDocument.setToolTipText("Leistungsnachweis in Dokument übertragen - Platzhalter {{ZE_TABELLE}} in einer 1x1-Tabelle einer Vorlage"); + cmdCreateTimesheetDocument.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cmdCreateTimesheetDocumentActionPerformed(evt); + } + }); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -1819,8 +1838,18 @@ public void mouseExited(java.awt.event.MouseEvent evt) { .addComponent(cmdViewDocument) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(lblTimesheet) + .addGap(18, 18, 18) + .addComponent(cmdCreateTimesheetDocument) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(cmdNavigateToDocument) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel15)) .addComponent(lblInvoiceDocument) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1830,12 +1859,7 @@ public void mouseExited(java.awt.event.MouseEvent evt) { .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblInvoiceDocumentCreated) .addComponent(lblInvoiceDocumentChanged)))) - .addGap(0, 0, Short.MAX_VALUE)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(cmdNavigateToDocument) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel15) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 228, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 233, Short.MAX_VALUE) .addComponent(cmdCreateInvoiceDocument) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cmbTableHeadersLanguage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -1864,7 +1888,13 @@ public void mouseExited(java.awt.event.MouseEvent evt) { .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel19) .addComponent(lblInvoiceDocumentChanged)) - .addContainerGap()) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 12, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblTimesheet) + .addComponent(cmdCreateTimesheetDocument)) + .addGap(422, 422, 422)) ); jTabbedPane1.addTab("Dokumente", jPanel1); @@ -1962,7 +1992,7 @@ public boolean isCellEditable(int rowIndex, int columnIndex) { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 604, Short.MAX_VALUE) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 604, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cmdCancel) @@ -2086,10 +2116,11 @@ private void fillCurrentEntry() throws Exception { this.currentEntry.setContact(this.recipientAddress); this.currentEntry.setCurrency(this.cmbCurrency.getSelectedItem().toString()); this.currentEntry.setSender(this.cmbInvoiceSender.getSelectedItem().toString()); - if(this.cmbPaymentMethods.getSelectedItem()!=null) + if (this.cmbPaymentMethods.getSelectedItem() != null) { this.currentEntry.setPaymentType(Invoice.paymentTypeForDisplayValue(this.cmbPaymentMethods.getSelectedItem().toString())); - else + } else { this.currentEntry.setPaymentType(Invoice.PAYMENTTYPE_OTHER); + } } private void save() { @@ -2160,10 +2191,11 @@ private StyledCalculationTable getTimesheetPositionsAsTable(List times = locator.lookupArchiveFileServiceRemote().getTimesheetPositionsForInvoice(this.currentEntry.getId()); + timesheetPosTable = this.getTimesheetPositionsAsTable(times, this.cmbTableHeadersLanguage.getSelectedItem().toString()); + } catch (Exception ex) { + log.error("error getting timesheet positions table", ex); + JOptionPane.showMessageDialog(this, "Fehler beim Laden Zeiterfassungspositionen: " + ex.getMessage(), com.jdimension.jlawyer.client.utils.DesktopUtils.POPUP_TITLE_ERROR, JOptionPane.ERROR_MESSAGE); + } + + ArchiveFileDocumentsBean timesheetDoc = this.caseView.newDocumentDialog(null, currentEntry, senderUser, this.getInvoicePositionsAsTable(this.cmbTableHeadersLanguage.getSelectedItem().toString()), timesheetPosTable, null, null); + + } + }//GEN-LAST:event_cmdCreateTimesheetDocumentActionPerformed + /** * @param args the command line arguments */ @@ -2832,6 +2897,7 @@ public void windowClosing(java.awt.event.WindowEvent e) { private javax.swing.JButton cmdCancel; private javax.swing.JButton cmdConfirmInvoiceNumber; private javax.swing.JButton cmdCreateInvoiceDocument; + private javax.swing.JButton cmdCreateTimesheetDocument; private javax.swing.JButton cmdDateCreated; private javax.swing.JButton cmdDateDue; private javax.swing.JButton cmdDatePeriodFrom; @@ -2876,6 +2942,7 @@ public void windowClosing(java.awt.event.WindowEvent e) { private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane jScrollPane3; private javax.swing.JScrollPane jScrollPane4; + private javax.swing.JSeparator jSeparator1; private javax.swing.JTabbedPane jTabbedPane1; private javax.swing.JLabel lblDirectDebit; private javax.swing.JPanel lblHeader; @@ -2889,6 +2956,7 @@ public void windowClosing(java.awt.event.WindowEvent e) { private javax.swing.JLabel lblNetValue; private javax.swing.JLabel lblPaymentsTotal; private javax.swing.JLabel lblRecipient; + private javax.swing.JLabel lblTimesheet; private javax.swing.JList lstPositionTemplates; private javax.swing.JPanel pnlInvoicePositions; private javax.swing.JPopupMenu popCalculations; @@ -2906,8 +2974,14 @@ public void onEvent(Event e) { if (this.currentEntry != null) { if (this.currentEntry.getId().equals(((InvoicePositionAddedEvent) e).getInvoiceId())) { this.addPosition(((InvoicePositionAddedEvent) e).getPosition()); + this.enableTimesheetButton(true); } } } } + + private void enableTimesheetButton(boolean enable) { + this.lblTimesheet.setEnabled(enable); + this.cmdCreateTimesheetDocument.setEnabled(enable); + } } diff --git a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.form b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.form index 2193b3cde..7f68998c5 100644 --- a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.form +++ b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.form @@ -59,6 +59,12 @@ + + + + + + @@ -68,7 +74,12 @@ - + + + + + + @@ -112,13 +123,14 @@ - +
- - - - - + + + + + +
@@ -129,5 +141,27 @@
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.java b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.java index b273dc2f9..cbdfb2cdb 100644 --- a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.java +++ b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetBillingDialog.java @@ -720,15 +720,23 @@ public TimesheetBillingDialog(JDialog parent, boolean modal, String caseId, Invo for (Timesheet ts : timesheets) { List positions = afs.getTimesheetPositions(ts.getId()); for (TimesheetPosition p : positions) { - if(p.getStopped()==null || p.getInvoice()!=null) + if(p.getStopped()==null) continue; - Object[] row = new Object[6]; + if(p.getInvoice()!=null && !p.isEditingAllowed()) + continue; + + Object[] row = new Object[7]; row[0] = false; row[1] = p.getTimesheet().getName(); row[2] = p; row[3] = p.getDescription(); row[4] = p.getTotal(); row[5] = df.format(p.getStarted()) + " - " + df.format(p.getStopped()); + String invoiceInfo=""; + if(p.getInvoice()!=null) { + invoiceInfo=p.getInvoice().getInvoiceNumber() + " (" + p.getInvoice().getStatusString() + (")"); + } + row[6] = invoiceInfo; ((DefaultTableModel) this.tblPositions.getModel()).addRow(row); } } @@ -756,6 +764,8 @@ private void initComponents() { cmdBilling = new javax.swing.JButton(); jScrollPane1 = new javax.swing.JScrollPane(); tblPositions = new javax.swing.JTable(); + cmdAll = new javax.swing.JButton(); + cmdNone = new javax.swing.JButton(); mnuSelect.setText("abrechnen"); mnuSelect.addActionListener(new java.awt.event.ActionListener() { @@ -793,22 +803,29 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { tblPositions.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { - {null, null, null, null, null, null}, - {null, null, null, null, null, null}, - {null, null, null, null, null, null}, - {null, null, null, null, null, null} + {null, null, null, null, null, null, null}, + {null, null, null, null, null, null, null}, + {null, null, null, null, null, null, null}, + {null, null, null, null, null, null, null} }, new String [] { - "abrechnen", "Projekt", "Position", "Beschreibung", "Gesamt", "Zeit" + "abrechnen", "Projekt", "Position", "Beschreibung", "Gesamt", "Zeit", "Beleg" } ) { Class[] types = new Class [] { - java.lang.Boolean.class, java.lang.String.class, java.lang.Object.class, java.lang.Object.class, java.lang.Float.class, java.lang.String.class + java.lang.Boolean.class, java.lang.String.class, java.lang.Object.class, java.lang.Object.class, java.lang.Float.class, java.lang.String.class, java.lang.String.class + }; + boolean[] canEdit = new boolean [] { + true, false, false, false, false, false, false }; public Class getColumnClass(int columnIndex) { return types [columnIndex]; } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit [columnIndex]; + } }); tblPositions.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { @@ -820,6 +837,22 @@ public void mousePressed(java.awt.event.MouseEvent evt) { }); jScrollPane1.setViewportView(tblPositions); + cmdAll.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons16/baseline_select_all_black_48dp.png"))); // NOI18N + cmdAll.setToolTipText("alle auswählen"); + cmdAll.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cmdAllActionPerformed(evt); + } + }); + + cmdNone.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons16/baseline_select_none_black_48dp.png"))); // NOI18N + cmdNone.setToolTipText("keine auswählen"); + cmdNone.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cmdNoneActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -832,14 +865,23 @@ public void mousePressed(java.awt.event.MouseEvent evt) { .addComponent(cmdBilling) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cmdCancel)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 776, Short.MAX_VALUE)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 776, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(cmdAll) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cmdNone) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 440, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cmdAll) + .addComponent(cmdNone)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 406, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cmdCancel) @@ -938,6 +980,18 @@ private void cmdBillingActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI this.dispose(); }//GEN-LAST:event_cmdBillingActionPerformed + private void cmdAllActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmdAllActionPerformed + for (int i = 0; i < this.tblPositions.getRowCount(); i++) { + this.tblPositions.setValueAt(true, i, 0); + } + }//GEN-LAST:event_cmdAllActionPerformed + + private void cmdNoneActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmdNoneActionPerformed + for (int i = 0; i < this.tblPositions.getRowCount(); i++) { + this.tblPositions.setValueAt(false, i, 0); + } + }//GEN-LAST:event_cmdNoneActionPerformed + /** * @param args the command line arguments */ @@ -979,8 +1033,10 @@ public void windowClosing(java.awt.event.WindowEvent e) { } // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cmdAll; private javax.swing.JButton cmdBilling; private javax.swing.JButton cmdCancel; + private javax.swing.JButton cmdNone; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JMenuItem mnuDeselect; private javax.swing.JMenuItem mnuSelect; diff --git a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetDialog.java b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetDialog.java index 44f5599cd..13be035a8 100644 --- a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetDialog.java +++ b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetDialog.java @@ -672,6 +672,7 @@ You should also get your employer (if you work as a programmer) or school, import com.jdimension.jlawyer.persistence.AppOptionGroupBean; import com.jdimension.jlawyer.persistence.AppUserBean; import com.jdimension.jlawyer.persistence.ArchiveFileBean; +import com.jdimension.jlawyer.persistence.Invoice; import com.jdimension.jlawyer.persistence.Timesheet; import com.jdimension.jlawyer.persistence.TimesheetPosition; import com.jdimension.jlawyer.server.constants.OptionConstants; @@ -1266,11 +1267,15 @@ private boolean save() { ClientSettings settings = ClientSettings.getInstance(); List allPositions = new ArrayList<>(); + List invoicesThatRequireUpdate=new ArrayList<>(); for (Component c : this.pnlTimesheetPositions.getComponents()) { if (c instanceof TimesheetPositionEntryPanel) { ((TimesheetPositionEntryPanel) c).updateEntryTotal(Integer.parseInt(this.cmbTimesheetInterval.getSelectedItem().toString())); allPositions.add(((TimesheetPositionEntryPanel) c).getEntry()); - + + Invoice inv=((TimesheetPositionEntryPanel) c).invoiceUpdateRequired(); + if(inv!=null) + invoicesThatRequireUpdate.add(inv); } } @@ -1296,6 +1301,20 @@ private boolean save() { return false; } + if(!invoicesThatRequireUpdate.isEmpty()) { + StringBuilder sb=new StringBuilder(); + sb.append("").append("Für folgende Belege muss ein erneuter Import der Zeiten erfolgen: ").append("
    "); + ArrayList listed=new ArrayList<>(); + for(Invoice inv: invoicesThatRequireUpdate) { + if(!listed.contains(inv.getInvoiceNumber())) { + listed.add(inv.getInvoiceNumber()); + sb.append("
  • ").append(inv.getInvoiceNumber()).append("
  • "); + } + } + sb.append("
"); + JOptionPane.showMessageDialog(this, sb.toString(), com.jdimension.jlawyer.client.utils.DesktopUtils.POPUP_TITLE_HINT, JOptionPane.INFORMATION_MESSAGE); + } + return true; } diff --git a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.form b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.form index cd60fc268..666f8bbee 100644 --- a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.form +++ b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.form @@ -66,8 +66,7 @@ - - + @@ -77,6 +76,7 @@ + @@ -240,7 +240,7 @@ - + diff --git a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.java b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.java index de7b09f8f..f3d4c50e4 100644 --- a/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.java +++ b/j-lawyer-client/src/com/jdimension/jlawyer/client/editors/files/TimesheetPositionEntryPanel.java @@ -667,10 +667,12 @@ You should also get your employer (if you work as a programmer) or school, import com.jdimension.jlawyer.client.configuration.UserListCellRenderer; import com.jdimension.jlawyer.client.settings.ClientSettings; import com.jdimension.jlawyer.client.utils.ComponentUtils; +import com.jdimension.jlawyer.persistence.Invoice; import com.jdimension.jlawyer.persistence.TimesheetPosition; import com.jdimension.jlawyer.services.JLawyerServiceLocator; import java.awt.Container; import java.math.BigDecimal; +import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Date; @@ -687,35 +689,37 @@ You should also get your employer (if you work as a programmer) or school, * @author jens */ public class TimesheetPositionEntryPanel extends javax.swing.JPanel { - - private static final Logger log=Logger.getLogger(TimesheetPositionEntryPanel.class.getName()); - private TimesheetDialog dlgParent=null; - + private static final Logger log = Logger.getLogger(TimesheetPositionEntryPanel.class.getName()); + + private TimesheetDialog dlgParent = null; + private TimesheetPosition position = null; private String timesheetId = null; - - private DecimalFormat taxRateFormat=null; + + private DecimalFormat taxRateFormat = null; /** * Creates new form TimesheetPositionEntryPanel + * * @param dlgParent * @param taxRates + * @param sortedPrincipalIds */ public TimesheetPositionEntryPanel(TimesheetDialog dlgParent, List taxRates, List sortedPrincipalIds) { initComponents(); - - this.dlgParent=dlgParent; - + + this.dlgParent = dlgParent; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMAN); this.taxRateFormat = (DecimalFormat) nf; this.taxRateFormat.applyPattern("0.0"); - - String []principalItems = sortedPrincipalIds.toArray(new String[0]); + + String[] principalItems = sortedPrincipalIds.toArray(new String[0]); OptionsComboBoxModel assistModel = new OptionsComboBoxModel(principalItems); this.cmbPrincipal.setModel(assistModel); this.cmbPrincipal.setRenderer(new UserListCellRenderer()); - + this.txtUnitPrice.getDocument().addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { @@ -749,7 +753,7 @@ public void insertUpdate(DocumentEvent e) { updateParentTotal(); } }); - + this.txtStopped.getDocument().addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { @@ -766,7 +770,7 @@ public void insertUpdate(DocumentEvent e) { updateParentTotal(); } }); - + this.cmbTaxRate.removeAllItems(); taxRates.forEach(tr -> { this.cmbTaxRate.addItem(tr); @@ -775,71 +779,94 @@ public void insertUpdate(DocumentEvent e) { } public void setEntry(String timesheetId, TimesheetPosition pos, int intervalMinutes) { - - this.timesheetId=timesheetId; - - this.position = pos; - this.txtName.setText(pos.getName()); - this.taDescription.setText(pos.getDescription()); - this.cmbTaxRate.setSelectedItem(pos.getTaxRate()); - this.txtUnitPrice.setValue(pos.getUnitPrice()); - this.txtStarted.setValue(pos.getStarted()); - this.txtStopped.setValue(pos.getStopped()); - - if (!ComponentUtils.containsItem(cmbPrincipal, pos.getPrincipal())) { - this.cmbPrincipal.addItem(pos.getPrincipal()); - } - this.cmbPrincipal.setSelectedItem(pos.getPrincipal()); - - this.updateEntryTotal(intervalMinutes); - - if(pos.getInvoice()!=null) { - this.lblBilled.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/agt_action_success.png"))); - this.lblBilled.setText(""); - this.lblBilled.setToolTipText("abgerechnet mit Rechnung " + pos.getInvoice().getInvoiceNumber()); - this.cmbPrincipal.setEnabled(false); - this.txtName.setEnabled(false); - this.txtStarted.setEnabled(false); - this.txtStopped.setEnabled(false); - this.txtUnitPrice.setEnabled(false); - this.taDescription.setEnabled(false); - this.cmbTaxRate.setEnabled(false); - this.lblIndicator.setBackground(DefaultColorTheme.COLOR_LOGO_GREEN); - } else { - this.lblBilled.setIcon(null); - this.lblBilled.setText(""); - this.lblBilled.setToolTipText(null); - this.cmbPrincipal.setEnabled(true); - this.txtName.setEnabled(true); - this.txtStarted.setEnabled(true); - this.txtStopped.setEnabled(true); - this.txtUnitPrice.setEnabled(true); - this.taDescription.setEnabled(true); - this.cmbTaxRate.setEnabled(true); - this.lblIndicator.setBackground(DefaultColorTheme.COLOR_DARK_GREY); - } - + + this.timesheetId = timesheetId; + + this.position = pos; + this.txtName.setText(pos.getName()); + this.taDescription.setText(pos.getDescription()); + this.cmbTaxRate.setSelectedItem(pos.getTaxRate()); + this.txtUnitPrice.setValue(pos.getUnitPrice()); + this.txtStarted.setValue(pos.getStarted()); + this.txtStopped.setValue(pos.getStopped()); + + if (!ComponentUtils.containsItem(cmbPrincipal, pos.getPrincipal())) { + this.cmbPrincipal.addItem(pos.getPrincipal()); + } + this.cmbPrincipal.setSelectedItem(pos.getPrincipal()); + + this.updateEntryTotal(intervalMinutes); + + boolean editingAllowed = false; + if (pos.getInvoice() != null && pos.isEditingAllowed()) { + // assigned to an invoice, but editing allowed + editingAllowed = true; + this.lblBilled.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/file_doc.png"))); + this.lblBilled.setText(pos.getInvoice().getInvoiceNumber()); + this.lblBilled.setToolTipText("abgerechnet mit Rechnung " + pos.getInvoice().getInvoiceNumber() + " - Rechnungsstatus ermöglicht das Bearbeiten der erfassten Zeit"); + this.lblIndicator.setBackground(DefaultColorTheme.COLOR_LOGO_BLUE); + + } else if (pos.getInvoice() != null && !pos.isEditingAllowed()) { + // assigned to an invoice, no longer editable + editingAllowed = false; + this.lblBilled.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/file_doc.png"))); + this.lblBilled.setText(pos.getInvoice().getInvoiceNumber()); + this.lblBilled.setToolTipText("abgerechnet mit Rechnung " + pos.getInvoice().getInvoiceNumber() + " - Rechnungsstatus verhindert das Bearbeiten der erfassten Zeit"); + this.lblIndicator.setBackground(DefaultColorTheme.COLOR_LOGO_GREEN); + } else { + // not assigned to an invoice + editingAllowed = true; + this.lblBilled.setIcon(null); + this.lblBilled.setText(""); + this.lblBilled.setToolTipText(null); + this.lblIndicator.setBackground(DefaultColorTheme.COLOR_DARK_GREY); + } + + this.cmbPrincipal.setEnabled(editingAllowed); + this.txtName.setEnabled(true); + this.txtStarted.setEnabled(editingAllowed); + this.txtStopped.setEnabled(editingAllowed); + this.txtUnitPrice.setEnabled(editingAllowed); + this.taDescription.setEnabled(true); + this.cmbTaxRate.setEnabled(editingAllowed); + } - + public boolean hasInvoice() { - if(this.position!=null) { - return this.position.getInvoice()!=null; + if (this.position != null) { + return this.position.getInvoice() != null; } return false; } - public TimesheetPosition getEntry() { - + public Invoice invoiceUpdateRequired() { if(this.position==null) return null; - TimesheetPosition clone=new TimesheetPosition(); + if(this.position.getInvoice()==null) + return null; + + BigDecimal currentTotal=this.position.getTotal(); + BigDecimal newTotal=BigDecimal.valueOf((Float) this.txtTotal.getValue()).setScale(2, RoundingMode.HALF_UP); + if(!(currentTotal.compareTo(newTotal)==0)) + return this.position.getInvoice(); + + return null; + } + + public TimesheetPosition getEntry() { + + if (this.position == null) { + return null; + } + + TimesheetPosition clone = new TimesheetPosition(); clone.setId(this.position.getId()); clone.setTimesheet(this.position.getTimesheet()); - clone.setStarted((Date)this.txtStarted.getValue()); - clone.setStopped((Date)this.txtStopped.getValue()); + clone.setStarted((Date) this.txtStarted.getValue()); + clone.setStopped((Date) this.txtStopped.getValue()); clone.setPrincipal(this.cmbPrincipal.getSelectedItem().toString()); - + clone.setName(this.txtName.getText()); clone.setDescription(this.taDescription.getText()); try { @@ -849,12 +876,13 @@ public TimesheetPosition getEntry() { JOptionPane.showMessageDialog(this, "fehlerhafter Steuersatz: " + ex.getMessage(), com.jdimension.jlawyer.client.utils.DesktopUtils.POPUP_TITLE_ERROR, JOptionPane.ERROR_MESSAGE); } clone.setTotal(BigDecimal.valueOf((Float) this.txtTotal.getValue())); - - if(this.txtUnitPrice.getValue()==null) + + if (this.txtUnitPrice.getValue() == null) { clone.setUnitPrice(BigDecimal.ZERO); - else + } else { clone.setUnitPrice(BigDecimal.valueOf(((Number) this.txtUnitPrice.getValue()).floatValue())); - + } + return clone; } @@ -936,7 +964,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { txtStopped.setFont(txtStopped.getFont().deriveFont(txtStopped.getFont().getSize()-2f)); lblBilled.setFont(lblBilled.getFont().deriveFont(lblBilled.getFont().getStyle() | java.awt.Font.BOLD)); - lblBilled.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/agt_action_success.png"))); // NOI18N + lblBilled.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/file_doc.png"))); // NOI18N lblBilled.setText(" "); cmbPrincipal.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); @@ -992,8 +1020,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblBilled) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(txtStarted, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel4) @@ -1001,7 +1028,8 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addComponent(jLabel3) .addComponent(txtTotal, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(txtUnitPrice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel2))) + .addComponent(jLabel2)) + .addComponent(lblBilled, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(lblIndicator, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -1009,7 +1037,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { }// //GEN-END:initComponents private void cmdRemovePositionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmdRemovePositionActionPerformed - + int response = JOptionPane.showConfirmDialog(this, "Position '" + txtName.getText() + "' (" + txtStarted.getText() + ") löschen?", "erfasste Zeit löschen", JOptionPane.YES_NO_OPTION); if (response == JOptionPane.YES_OPTION) { @@ -1035,7 +1063,7 @@ private void cmdRemovePositionActionPerformed(java.awt.event.ActionEvent evt) {/ private void cmbTaxRateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmbTaxRateActionPerformed this.updateParentTotal(); }//GEN-LAST:event_cmbTaxRateActionPerformed - + public void updateParentTotal() { this.dlgParent.updateTotals(this); } @@ -1046,15 +1074,15 @@ public void updateEntryTotal(int intervalMinutes) { try { unitPrice = (Number) this.txtUnitPrice.getValue(); - if (this.txtStarted.getValue()!=null && this.txtStopped.getValue()!=null && unitPrice!=null) { + if (this.txtStarted.getValue() != null && this.txtStopped.getValue() != null && unitPrice != null) { this.txtUnitPrice.putClientProperty(FlatClientProperties.OUTLINE, null); this.txtTotal.putClientProperty(FlatClientProperties.OUTLINE, null); - Date started=(Date)this.txtStarted.getValue(); - Date stopped=(Date)this.txtStopped.getValue(); - float totalMinutes=((float)(stopped.getTime()-started.getTime()))/1000f/60f; - double roundedMinutes=Math.ceil(totalMinutes/intervalMinutes)*intervalMinutes; - float roundedMinutesFloat=new Float(roundedMinutes); - float total=roundedMinutesFloat/60f*unitPrice.floatValue(); + Date started = (Date) this.txtStarted.getValue(); + Date stopped = (Date) this.txtStopped.getValue(); + float totalMinutes = ((float) (stopped.getTime() - started.getTime())) / 1000f / 60f; + double roundedMinutes = Math.ceil(totalMinutes / intervalMinutes) * intervalMinutes; + float roundedMinutesFloat = new Float(roundedMinutes); + float total = roundedMinutesFloat / 60f * unitPrice.floatValue(); this.txtTotal.setValue(total); } else { this.txtUnitPrice.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR); @@ -1066,7 +1094,6 @@ public void updateEntryTotal(int intervalMinutes) { this.txtTotal.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR); this.txtTotal.setValue(0f); } - } diff --git a/j-lawyer-server-entities/src/java/com/jdimension/jlawyer/persistence/TimesheetPosition.java b/j-lawyer-server-entities/src/java/com/jdimension/jlawyer/persistence/TimesheetPosition.java index cee3d3980..cb597531c 100644 --- a/j-lawyer-server-entities/src/java/com/jdimension/jlawyer/persistence/TimesheetPosition.java +++ b/j-lawyer-server-entities/src/java/com/jdimension/jlawyer/persistence/TimesheetPosition.java @@ -920,6 +920,18 @@ public void setStopped(Date stopped) { public boolean isRunning() { return this.started!=null && this.stopped==null; } + + public boolean isEditingAllowed() { + if(this.invoice==null) + return true; + + if(this.invoice.getStatus()==Invoice.STATUS_CANCELLED || this.invoice.getStatus()==Invoice.STATUS_NEW) { + return true; + } else if(this.invoice.getStatus()==Invoice.STATUS_OPEN || this.invoice.getStatus()==Invoice.STATUS_OPEN_NONENFORCEABLE || this.invoice.getStatus()==Invoice.STATUS_OPEN_REMINDER1 || this.invoice.getStatus()==Invoice.STATUS_OPEN_REMINDER2 || this.invoice.getStatus()==Invoice.STATUS_OPEN_REMINDER3 || this.invoice.getStatus()==Invoice.STATUS_PAID) { + return false; + } + return true; + } /** * @return the invoice