1

I'm writing an invoice class and I want to keep the actual tex file as clean as possible, so that the code does not scare off the non-techies.

My ultimative goal would be to define a variable in the invoice.cls file and later set a value to it by a very simple and clean command in the main.tex. Something like \address{Street, Number}{ZIP Code Town}{Country} and \name{First name}{Last name} and \bankDetails{IBAN number}{Bank Swift Code}

Keep in mind, that I have to use the values of these variables in the invoice.cls file.

At the moment I defined a newcommand in the class file and gave it some optional arguments. By calling the command and passing the arguments I had a code like the one above. The thing is, that I need to access the name variable at multiple places and I don't want that the end user has to enter his name several times as arguments... It would be nice to define these variables globally and access them whenever I need them.

Like with

\newcommand{\firstName}{John}
\newcommand{\lastName}{Doe}
\newcommand{\name}{\firstName\,\lastName}

But you'll understand that

\name{John}{Doe}

would be much cleaner.

  • 1
    I would use a keyval syntax as user interface, but if you want commands: check how \title, \date and \author store their argument in the standard classes. – Ulrike Fischer Aug 2 '17 at 13:16
  • 2
    Do you just want something like \newcommand\name[1]{\def\@name{#1}}? Then you can use \@name to refer to the author's name from within the class file. – Ian Thompson Aug 2 '17 at 13:48
7

I am a great fan of expl3 and its property 'variable' feature, i.e. a key - based storage of data. As long as the amount of keys is not too large prop lists are very convenient.

A key might be FirstName or Zip.

The macro \StoreInvoiceData stores the keys given as FirstName=foo, IBAN=US345342342 etc into the relevant key slot of the property list, named \g_sam_invoice_prop here. The key-value-interface of the macro must not be confused with the keys of the property list, although the storage mechanism in the background is identical. The key= name is the same as the property key name, this is not mandatory but convenient in order to remember the names.

The order of specification does not matter at all.

Use \GetInvoiceData{foo} for example in order to extract the value of the foo key.

\documentclass{article}


\usepackage{xparse}

\ExplSyntaxOn
\prop_new:N \g_sam_invoice_prop

\cs_new:Nn \sam_store_property:nn {
  \prop_gput:Nnn \g_sam_invoice_prop {#1} {#2}
}

%Defining key-value interface
 \keys_define:nn {saminvoice} {
    Street .code:n = {\sam_store_property:nn {Street}{#1} },
    Number .code:n = {\sam_store_property:nn {Number}{#1} },
    Zip .code:n = {\sam_store_property:nn {Zip}{#1} },
    Country .code:n = {\sam_store_property:nn {Country}{#1} },
    FirstName .code:n = {\sam_store_property:nn {FirstName}{#1} },    
    SecondName .code:n = {\sam_store_property:nn {SecondName}{#1} },    
    IBAN .code:n = {\sam_store_property:nn {IBAN}{#1} },    
    BIC .code:n = {\sam_store_property:nn {BIC}{#1} },    
  }
\NewDocumentCommand{\StoreInvoiceData}{+m}{
  \prop_gclear:N \g_sam_invoice_prop% Clearing the property list
  % Set the keys
  \keys_set:nn {saminvoice}{#1}
}

\NewDocumentCommand{\GetInvoiceData}{m}{
  \prop_if_in:NnTF \g_sam_invoice_prop {#1} 
  {% True
    \prop_item:Nn \g_sam_invoice_prop {#1}% Extract the key #1 from the property list
  }{% False
    Unknown
  }
}

\ExplSyntaxOff

\begin{document}

\StoreInvoiceData{FirstName=Gandalf,SecondName={The Grey},Zip={The Nine},Country={Middle Earth}, IBAN={MORDOR6.60-34}}


This is an invoice for \GetInvoiceData{FirstName}\ \GetInvoiceData{SecondName} about the manufacturing and delivery of 100 Mage staffs. Please pay to \GetInvoiceData{IBAN}

\end{document}

enter image description here

Version for checking whether a key is given

\documentclass{article}


\usepackage{xparse}

\ExplSyntaxOn
\prop_new:N \g_sam_invoice_prop

\cs_new:Nn \sam_store_property:nn {
  \prop_gput:Nnn \g_sam_invoice_prop {#1} {#2}
}

%Defining key-value interface
 \keys_define:nn {saminvoice} {
    Street .code:n = {\sam_store_property:nn {Street}{#1} },
    Number .code:n = {\sam_store_property:nn {Number}{#1} },
    Zip .code:n = {\sam_store_property:nn {Zip}{#1} },
    Country .code:n = {\sam_store_property:nn {Country}{#1} },
    FirstName .code:n = {\sam_store_property:nn {FirstName}{#1} },    
    SecondName .code:n = {\sam_store_property:nn {SecondName}{#1} },    
    IBAN .code:n = {\sam_store_property:nn {IBAN}{#1} },    
    BIC .code:n = {\sam_store_property:nn {BIC}{#1} },    
    CompanyName .code:n= {\sam_store_property:nn {CompanyName}{#1} },
    ClientName .code:n= {\sam_store_property:nn {ClientName}{#1} }
  }
\NewDocumentCommand{\StoreInvoiceData}{+m}{
  \prop_gclear:N \g_sam_invoice_prop% Clearing the property list
  % Set the keys
  \keys_set:nn {saminvoice}{#1}
}

\NewDocumentCommand{\GetInvoiceData}{m}{
  \prop_if_in:NnTF \g_sam_invoice_prop {#1} 
  {% True
    \prop_item:Nn \g_sam_invoice_prop {#1}% Extract the key #1 from the property list
  }{% False
    Unknown
  }
}

% Normally better with a \prg_new_condiditional:, but for simplicity

% Check if the field is given and execute #2 for true case, otherwise #3
\NewDocumentCommand{\IfInvoiceFieldGivenTF}{m+m+m}{%
  \prop_if_in:NnTF \g_sam_invoice_prop {
    #1% Check to check
  }{% True #2
    #2
  }{% False
    #3
  }
}% End of \IfInVoiceFieldGivenT


% Do something only if #1 is given 
\NewDocumentCommand{\IfInvoiceFieldGivenT}{m+m}{%
  \prop_if_in:NnT \g_sam_invoice_prop {
    #1% Check to check
  }{% True #2
    #2
  }%
}% End of \IfInVoiceFieldGivenT



\ExplSyntaxOff

\begin{document}

\StoreInvoiceData{FirstName=Gandalf,SecondName={The Grey},Zip={The Nine},Country={Middle Earth}, IBAN={MORDOR6.60-34},CompanyName={LembasBread Ltd.}, ClientName={Elrond}}


This is an invoice for \GetInvoiceData{FirstName}\ \GetInvoiceData{SecondName} about the manufacturing and delivery of 100 Mage staffs. Please pay to \GetInvoiceData{IBAN}

\textbf{Long version:}

\IfInvoiceFieldGivenTF{CompanyName}{%
  \GetInvoiceData{CompanyName} c/o \GetInvoiceData{ClientName}
}{%
  \GetInvoiceData{ClientName}%
}%

\textbf{Short version:}

\IfInvoiceFieldGivenT{CompanyName}{%
  \GetInvoiceData{CompanyName} c/o{}% Extra space on purpose here
}
\GetInvoiceData{ClientName}

Clearing Data

\StoreInvoiceData{FirstName=Gandalf,SecondName={The Grey},Zip={The Nine},Country={Middle Earth}, IBAN={MORDOR6.60-34},ClientName={Elrond}}


\textbf{Long version:}

\IfInvoiceFieldGivenTF{CompanyName}{%
  \GetInvoiceData{CompanyName} c/o \GetInvoiceData{ClientName}
}{%
  \GetInvoiceData{ClientName}%
}%

\textbf{Short version:}

\IfInvoiceFieldGivenT{CompanyName}{%
  \GetInvoiceData{CompanyName} c/o{}% Extra space on purpose here
}
\GetInvoiceData{ClientName}


\end{document}

enter image description here

  • \keys_define:nn should be outside the definition of \StoreInvoiceData. In this case it's not a big deal, but in general situations it would be. – egreg Aug 3 '17 at 7:35
  • 2
    No doubt Gandalf also won the Rivendell lottery (but has to pay a fee to claim his prize), and has an INVESTMENT OPPORTUNITY; he just needs to send 1,000 gold pieces to Dol Guldur and wait for 1,000,000 to be transferred into his account. – Ian Thompson Aug 3 '17 at 9:30
  • @IanThompson: Are you sure this is Rivendell lottery then? Sounds rather like pyramid sales :-D – user31729 Aug 3 '17 at 12:28
  • @egreg: You're right, I shifted it outside of the macro – user31729 Aug 3 '17 at 12:37
  • 1
    That's awesome! – Sam Aug 4 '17 at 15:47
2

Take a look at the definitions for the data of \maketitle:

\def\title#1{\gdef\@title{#1}}
\def\@title{\@latex@error{No \noexpand\title given}\@ehc}
\def\author#1{\gdef\@author{#1}}
\def\@author{\@latex@warning@no@line{No \noexpand\author given}}
\def\date#1{\gdef\@date{#1}}
\gdef\@date{\today}

\title{My Title} stores My Title in the global macro \@title. The latter is used in \maketitle. If the user has forgotten to call \title, \@title is initialized in such a way, that it throws an error message. The class file would used \ClassError{class name}{Error message} instead of \@latex@error. Example:

\ProvidesClass{foobar}[2017/08/05 v1.0 ...]
...
\newcommand*{\name}[2]{%
  \gdef\@firstname{#1}%
  \gdef\@lastname{#2}%
}
\def\@firstname{%
  \ClassError{foobar}{No \noexpand\name given.}%
}
\def\@lastname{%
  \ClassError{foobar}{No \noexpand\name given.}%
}

Usage:

\documentclass[...]{foobar}
...
\name{First name}{Last name}

Then \@firstname and \@lastname can be used (by class macros, for example).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.