treyhunner / invoices

Simple LaTeX invoice format
MIT License
88 stars 24 forks source link

math error #2

Open lungetech opened 10 years ago

lungetech commented 10 years ago

The addition provided by the template is wrong.

100.08+81.32+65.68+121.97+50.04 = 419.09

The PDF shows the subtotal as 419.08.

johannesbottcher commented 9 years ago

Unfortunately, still not sorted as seen by the following two examples, one running with the invoice class here, the other running with the invoice class available at latex-templates.com. A post at LateX-Community.org made me aware.

Running here

\documentclass{invoice}
\begin{document}
\hourlyrate{180}
\begin{invoiceTable}
\hourrow{January 3, 2011}{1}
\hourrow{January 7, 2011}{.7}
\end{invoiceTable}
\end{document}

older invoice class distributed at latex-templates


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Invoice Template
% LaTeX Template
% Version 1.0 (3/11/12)
%
% This template has been downloaded from:
% http://www.LaTeXTemplates.com
%
% Original author:
% Trey Hunner (http://www.treyhunner.com/)
%
% License:
% CC BY-NC-SA 3.0 (http://creativecommons.org/licenses/by-nc-sa/3.0/)
%
% Important note:
% This template requires the invoice.cls file to be in the same directory as 
% the .tex file. The invoice.cls file provides the style used for structuring the
% document.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\documentclass{invoice} % Use the custom invoice class (invoice.cls)
\begin{document}
\begin{invoiceTable}
    \hourrow{bla bla}{1.0}{180}
%   \hourrow{bla bla}{0.20}{180}
    \hourrow{bla bla}{0.70}{180}
    \subtotal
\end{invoiceTable}
\end{document}

Thanks for taking a look at this.

johannesbottcher commented 9 years ago

New instance of the problem at TeX.Stackexchange

Quoting @ufischer:

In the version on github \hourrow has only two arguments and I don't get your rounding error. But the example in the issues tracker still gives wrong results. The math in the package is faulty. It uses \real from the calc package to handle decimals like 0.7 and seems not to know that you can get rounding errors and that multiplying everything with 1000 isn't enough. Throw it away. One would have to replace all calculations with something better to get something usable.

treyhunner commented 9 years ago

Thanks for adding more context @johannesbottcher. I haven't experienced this problem before, but I can't argue against the math in this package probably being faulty. I am a LaTeX novice and I don't actually know what I'm doing. :sweat_smile:

If anyone has a solution for the math errors, I'd gladly welcome a pull request. :smile_cat:

eg9 commented 7 years ago

Here is a fixed version for your class

%%% modified by egreg for TeX.Stackexchange
%%% http://tex.stackexchange.com/questions/331898/
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Copyright (c) 2011 Trey Hunner                                          %
%                                                                          %
%  Permission is hereby granted, free of charge, to any person obtaining   %
%  a copy of this software and associated documentation files (the         %
%  "Software"), to deal in the Software without restriction, including     %
%  without limitation the rights to use, copy, modify, merge, publish,     %
%  distribute, sublicense, and/or sell copies of the Software, and to      %
%  permit persons to whom the Software is furnished to do so, subject to   %
%  the following conditions:                                               %
%                                                                          %
%  The above copyright notice and this permission notice shall be          %
%  included in all copies or substantial portions of the Software.         %
%                                                                          %
%  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,         %
%  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF      %
%  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                   %
%  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE  %
%  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION  %
%  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION   %
%  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.         %
%                                                                          %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\ProvidesClass{invoice3}

\LoadClass[12pt]{article}

\usepackage[letterpaper,hmargin=0.79in,vmargin=0.79in]{geometry}
\usepackage[parfill]{parskip} % Do not indent paragraphs
\usepackage{xparse} % Fixed-point arithmetic, among many other things
\usepackage{siunitx} % for displaying numbers
\usepackage{longtable}

\pagestyle{empty} % No page numbers
\linespread{1.5}

\setlength{\doublerulesep}{\arrayrulewidth} % Double rules look like one thick one

% Command for setting a default hourly rate
\ExplSyntaxOn
\NewDocumentCommand{\hourlyrate}{m}
 {
  \fp_set:Nn \l_invoice_hourlyrate_fp { #1 }
 }
\fp_new:N \l_invoice_hourlyrate_fp
\hourlyrate{1} % initialize

\NewDocumentCommand{\feetype}{m}
 {
  \textbf{#1} \\
 }

% Counters for totaling up hours and dollars
\fp_new:N \g_invoice_hours_fp
\fp_new:N \g_invoice_subhours_fp
\fp_new:N \g_invoice_cost_fp
\fp_new:N \g_invoice_subcost_fp

% Formats input number with 2 digits after the decimal place
\NewDocumentCommand{\formatNumber}{m}
 {
  \num[detect-all,round-integer-to-decimal,round-mode=places,round-precision=2]{\fp_eval:n {#1}}
 }

% Returns the total of counter
\NewDocumentCommand{\total}{m}
 {
  \formatNumber{#1}
 }

% Create a new row from title, unit quantity, unit rate, and unit name
\NewDocumentCommand{\unitrow}{mmmm}
 {
  \fp_gadd:Nn \g_invoice_cost_fp { (#2) * (#3) }
  \fp_gadd:Nn \g_invoice_subcost_fp { (#2) * (#3) }
  #1 &
  \formatNumber{#2} ~ #4 &
  \$\formatNumber{#3} &
  \$\formatNumber{#2 * #3}
  \\
 }
% Create a new row from title and expense amount
\NewDocumentCommand{\feerow}{mm}
 {
  \fp_gadd:Nn \g_invoice_cost_fp { #2 }
  \fp_gadd:Nn \g_invoice_subcost_fp { #2 }
  #1 & & \$\formatNumber{#2} & \$\formatNumber{#2} \\
 }
\DeclareExpandableDocumentCommand{\subtotal}{}
 {
  \hline
  \subtotalaux
 }
\NewDocumentCommand{\subtotalaux}{s}
 {
  \textbf{Subtotal} &
  \IfBooleanF{#1}{\textbf{\total{\g_invoice_subhours_fp} ~ hours}} &
  &
  \textbf{\$\formatNumber{\g_invoice_subcost_fp}}
  \fp_gzero:N \g_invoice_subcost_fp
  \IfBooleanF{#1}{\fp_gzero:N \g_invoice_subhours_fp}
  \\*[1.5ex]
 }
% Create a new row from date and hours worked (use stored fee type and hourly rate)
\NewDocumentCommand{\hourrow}{mm}
 {
  \fp_gadd:Nn \g_invoice_hours_fp { #2 }
  \fp_gadd:Nn \g_invoice_subhours_fp { #2 }
  \unitrow{#1}{#2}{\l_invoice_hourlyrate_fp}{hours}
 }

% Create an invoice table
\NewDocumentEnvironment{invoiceTable}{}
 {
  \setlength{\tabcolsep}{0.8ex}
  \setlength\LTleft{0pt}
  \setlength\LTright{0pt}
  \begin{longtable}{@{\extracolsep{\fill}\hspace{\tabcolsep}} l r r r }
  \hline
  \textbf{Description~of~Services} &
  \multicolumn{1}{c}{\bfseries Quantity} &
  \multicolumn{1}{c}{\bfseries Unit~Price} &
  \multicolumn{1}{c}{\bfseries Amount} \\*
  \hline\hline
  \endhead
 }
 {
  \\*[-\normalbaselineskip]
  \hline\hline\hline
  {\bfseries Balance Due} & & & {\bfseries \$\formatNumber{\g_invoice_cost_fp}} \\
  \end{longtable}
 }
\ExplSyntaxOff
eg9 commented 7 years ago

Here's another version using only fp

%%% modified by egreg
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Copyright (c) 2011 Trey Hunner                                          %
%                                                                          %
%  Permission is hereby granted, free of charge, to any person obtaining   %
%  a copy of this software and associated documentation files (the         %
%  "Software"), to deal in the Software without restriction, including     %
%  without limitation the rights to use, copy, modify, merge, publish,     %
%  distribute, sublicense, and/or sell copies of the Software, and to      %
%  permit persons to whom the Software is furnished to do so, subject to   %
%  the following conditions:                                               %
%                                                                          %
%  The above copyright notice and this permission notice shall be          %
%  included in all copies or substantial portions of the Software.         %
%                                                                          %
%  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,         %
%  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF      %
%  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                   %
%  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE  %
%  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION  %
%  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION   %
%  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.         %
%                                                                          %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\ProvidesClass{invoice}

\LoadClass[12pt]{article}

\usepackage[letterpaper,hmargin=0.79in,vmargin=0.79in]{geometry}
\usepackage[parfill]{parskip} % Do not indent paragraphs
\usepackage{fp} % Fixed-point arithmetic
\usepackage{longtable}

\pagestyle{empty} % No page numbers
\linespread{1.5}

\setlength{\doublerulesep}{\arrayrulewidth} % Double rules look like one thick one

% variables for total cost and subcost, total hours and subhours
\def\totalcost{0}\def\subcost{0}
\def\totalhours{0}\def\subhours{0}

% Command for setting a default hourly rate
\newcommand{\hourlyrate}[1]{\def \@hourlyrate {#1}}
\hourlyrate{1}
\newcommand{\feetype}[1]{
    \textbf{#1}
    \\
}

% Formats inputed number with 2 digits after the decimal place
\newcommand*{\formatNumber}[1]{\FPround{\temp}{#1}{2}\temp} %

% Returns the total of counter
\newcommand*{\total}[1]{\formatNumber{#1}}

% Create an invoice table
\newenvironment{invoiceTable}{%
    % Create a new row from title, unit quantity, unit rate, and unit name
    \newcommand*{\unitrow}[4]{%
         \FPmul{\tempa}{##2}{##3}%
         \FPadd{\tempb}{\totalcost}{\tempa}%
         \global\let\totalcost\tempb
         \FPadd{\tempb}{\subcost}{\tempa}%
         \global\let\subcost\tempb
         ##1 & \formatNumber{##2} ##4 & \$\formatNumber{##3} & \$\FPmul{\temp}{##2}{##3}\formatNumber{\temp}%
         \\
    }%
    % Create a new row from title and expense amount
    \newcommand*{\feerow}[2]{%
         \FPadd{\tempa}{\totalcost}{##2}%
         \global\let\totalcost\tempa
         \FPadd{\tempa}{\subcost}{##2}%
         \global\let\subcost\tempa
         ##1 & & \$\formatNumber{##2} & \$\formatNumber{##2}%
         \\
    }%
    \newcommand{\subtotalNoStar}{%
        \textbf{Subtotal} & \textbf{\total{\subhours} hours} &  & \textbf{\$\total{\subcost}}%
        \gdef\subcost{0}%
        \gdef\subhours{0}%
        \\*[1.5ex]
    }%
    \newcommand{\subtotalStar}{%
        \textbf{Subtotal} & & & \textbf{\$\total{\subcost}}
        \gdef\subcost{0}%
        \\*[1.5ex]
    }%
    \newcommand{\subtotal}{%
         \hline
         \@ifstar
         \subtotalStar
         \subtotalNoStar
    }%
    % Create a new row from date and hours worked (use stored fee type and hourly rate)
    \newcommand*{\hourrow}[2]{%
        \FPadd{\tempa}{\totalhours}{##2}%
        \global\let\totalhours\tempa
        \FPadd{\tempa}{\subhours}{##2}%
        \global\let\subhours\tempa
        \unitrow{##1}{##2}{\@hourlyrate}{hours}%
    }%
    \setlength{\tabcolsep}{0.8ex}%
    \setlength\LTleft{0pt}%
    \setlength\LTright{0pt}%
    \begin{longtable}{@{\extracolsep{\fill}\hspace{\tabcolsep}} l r r r }
    \hline
    \bfseries Description of Services & \multicolumn{1}{c}{\bfseries Quantity} & \multicolumn{1}{c}{\bfseries Unit Price} & \multicolumn{1}{c}{\bfseries Amount} \\*
    \hline\hline
    \endhead
}{
    \hline\hline\hline
    \bfseries Balance Due & & & \bfseries \$\total{\totalcost} \\
    \end{longtable}
}
pmcharrison commented 7 years ago

eg9's solution worked great for me, thanks!