root / pykota / trunk / bin / pkinvoice @ 2663

Revision 2663, 11.8 kB (checked in by jerome, 18 years ago)

The logo is now repeated on all pages but stored only once in the PDF document.
No more work today.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKota Invoice generator
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22#
23# $Id$
24#
25#
26
27import sys
28import os
29import pwd
30import cStringIO
31
32try :
33    from reportlab.pdfgen import canvas
34    from reportlab.lib import pagesizes
35    from reportlab.lib.units import cm
36except ImportError :   
37    hasRL = 0
38else :   
39    hasRL = 1
40   
41try :
42    import PIL.Image 
43except ImportError :   
44    hasPIL = 0
45else :   
46    hasPIL = 1
47
48from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
49from pykota.config import PyKotaConfigError
50from pykota.storage import PyKotaStorageError
51
52__doc__ = N_("""pkinvoice v%(__version__)s (c) %(__years__)s %(__author__)s
53
54An invoice generator for PyKota.
55
56command line usage :
57
58  pkinvoice [options] user1 user2 ... userN
59
60options :
61
62  -v | --version       Prints edpykota's version number then exits.
63  -h | --help          Prints this message then exits.
64 
65  -g | --groups        Generate invoices for users groups instead of users.
66 
67  -l | --logo img      Use the image as the banner's logo. The logo will
68                       be drawn at the center top of the page. The default
69                       logo is /usr/share/pykota/logos/pykota.jpeg
70                       
71  -p | --pagesize sz   Sets sz as the page size. Most well known
72                       page sizes are recognized, like 'A4' or 'Letter'
73                       to name a few. The default size is A4.
74                       
75  -n | --number N      Sets the number of the first invoice. This number
76                       will automatically be incremented for each invoice.
77                       
78  -r | --reference v   Uses v as the initial reference value : an invoice
79                       will be generated for any account balance's value
80                       lower or equal to the reference value. The
81                       default reference value is of course 0.0 credits.
82                       
83  -o | --output f.pdf  Defines the name of the invoice file which will
84                       be generated as a PDF document. If not set or
85                       set to '-', the PDF document is sent to standard
86                       output.
87                       
88  -u | --unit u        Defines the name of the unit to use on the invoice.                       
89                       The default unit is 'Credits', optionally translated
90                       to your native language if it is supported by PyKota.
91 
92  -V | --vat p         Sets the percent value of the applicable VAT to be
93                       exposed. The default is 0.0, meaning no VAT
94                       information will be included.
95                       
96  user1 through userN and group1 through groupN can use wildcards if
97  needed. If no user argument is used, a wildcard of '*' is assumed,
98  meaning include all users or groups.
99 
100examples :                       
101
102  $ pkinvoice --reference 15.0 --unit EURO --output invoices.pdf
103 
104  Will generate a PDF document containing invoices for all users whose
105  account balance is below 15.0 credits. For each user the amount due
106  will be (15.0 - AccountBalance) EURO when that value is positive.
107  No VAT information will be included.
108""") 
109       
110class PKInvoice(PyKotaTool) :       
111    """A class for pkinvoice."""
112    def getPageSize(self, pgsize) :
113        """Returns the correct page size or None if not found."""
114        try :
115            return getattr(pagesizes, pgsize.upper())
116        except AttributeError :   
117            try :
118                return getattr(pagesizes, pgsize.lower())
119            except AttributeError :
120                pass
121               
122    def pagePDF(self, invoicenumber, name, amount, vatamount) :
123        """Generates a new page in the PDF document."""
124        sys.stderr.write("#%06i    %s     %.2f    %.2f\n" % (invoicenumber, name, amount, vatamount))
125        self.canvas.doForm("background")
126        self.canvas.showPage()
127       
128    def initPDF(self, pagesize, logo) :
129        """Initializes the PDF document."""
130        self.pdfDocument = cStringIO.StringIO()       
131        self.canvas = c = canvas.Canvas(self.pdfDocument, pagesize=pagesize, pageCompression=1)
132       
133        c.setAuthor(pwd.getpwuid(os.geteuid())[0])
134        c.setTitle("PyKota invoices")
135        c.setSubject("This is an invoice generated with PyKota")
136       
137        xcenter = pagesize[0] / 2.0
138        ycenter = pagesize[1] / 2.0
139                   
140        ypos = pagesize[1] - (2 * cm)           
141       
142        self.canvas.beginForm("background")
143        self.canvas.saveState()
144       
145        if logo :
146            try :   
147                imglogo = PIL.Image.open(logo)
148            except :   
149                self.printInfo("Unable to open image %s" % logo, "warn")
150            else :
151                (width, height) = imglogo.size
152                multi = float(width) / (8 * cm) 
153                width = float(width) / multi
154                height = float(height) / multi
155                xpos = xcenter - (width / 2.0)
156                ypos -= height
157                c.drawImage(logo, xpos, ypos, width, height)
158       
159        # New top
160        xpos = pagesize[0] / 5.0
161        ypos -= (1 * cm) + 20
162       
163        self.canvas.restoreState()
164        self.canvas.endForm()
165       
166    def endPDF(self, fname) :   
167        """Flushes the PDF generator."""
168        self.canvas.save()
169        if fname != "-" :       
170            outfile = open(fname, "w")
171            outfile.write(self.pdfDocument.getvalue())
172            outfile.close()
173        else :   
174            sys.stdout.write(self.pdfDocument.getvalue())
175            sys.stdout.flush()
176       
177    def main(self, names, options) :
178        """Generate invoices."""
179        if not hasRL :
180            raise PyKotaToolError, "The ReportLab module is missing. Download it from http://www.reportlab.org"
181        if not hasPIL :
182            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads"
183           
184        if not self.config.isAdmin :
185            raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command."))
186       
187        try :   
188            vat = float(options["vat"])
189            if not (0.0 <= vat < 100.0) :
190                raise ValueError
191        except :   
192            raise PyKotaCommandLineError, _("Incorrect value '%s' for the --vat command line option") % options["vat"]
193           
194        try :   
195            reference = float(options["reference"])
196        except :   
197            raise PyKotaCommandLineError, _("Incorrect value '%s' for the --reference command line option") % options["reference"]
198           
199        try :   
200            number = float(options["number"])
201            if number <= 0 :
202                raise ValueError
203        except :   
204            raise PyKotaCommandLineError, _("Incorrect value '%s' for the --number command line option") % options["number"]
205           
206        pagesize = self.getPageSize(options["pagesize"])
207        if pagesize is None :
208            pagesize = self.getPageSize("a4")
209            self.printInfo(_("Invalid 'pagesize' option %s, defaulting to A4.") % options["pagesize"], "warn")
210           
211        if not names :
212            names = [ "*" ]
213           
214        outfname = options["output"]   
215        if outfname != "-" :
216            self.display("%s...\n" % _("Processing"))
217           
218        suffix = (options["groups"] and "Group") or "User"       
219        entries = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
220        if entries :
221            self.initPDF(pagesize, options["logo"].strip())
222            nbtotal = len(entries)
223            for i in range(nbtotal) :
224                entry = entries[i]
225                amount = reference - entry.AccountBalance
226                if amount > 0.0 :
227                    # There's something due !
228                    ht = ((amount * 10000.0) / (100.0 + vat)) / 100.0
229                    vatamount = amount - ht
230                    self.pagePDF(number, entry.Name, amount, vatamount)
231                    number += 1
232                if outfname != "-" :
233                    percent = 100.0 * float(i) / float(nbtotal)
234                    self.display("\r%.02f%%" % percent)
235                   
236            self.endPDF(outfname)
237           
238        if outfname != "-" :
239            self.display("\r100.00%%\r        ")
240            self.display("\r%s\n" % _("Done."))
241                     
242if __name__ == "__main__" : 
243    retcode = 0
244    try :
245        defaults = { "reference" : "0.0",
246                     "vat" : "0.0",
247                     "unit" : N_("Credits"),
248                     "output" : "-",
249                     "pagesize" : "a4", \
250                     "logo" : "/usr/share/pykota/logos/pykota.jpeg",
251                     "number" : "1",
252                   }
253        short_options = "vho:gr:u:V:p:l:n:"
254        long_options = ["help", "version", \
255                        "groups", "reference=", "unit=", "output=", \
256                        "pagesize=", "logo=", "vat=", "number="]
257       
258        # Initializes the command line tool
259        invoiceGenerator = PKInvoice(doc=__doc__)
260        invoiceGenerator.deferredInit()
261       
262        # parse and checks the command line
263        (options, args) = invoiceGenerator.parseCommandline(sys.argv[1:], short_options, long_options, allownothing=True)
264       
265        # sets long options
266        options["help"] = options["h"] or options["help"]
267        options["version"] = options["v"] or options["version"]
268       
269        options["groups"] = options["g"] or options["groups"]
270        options["reference"] = options["r"] or options["reference"] or defaults["reference"]
271        options["vat"] = options["V"] or options["vat"] or defaults["vat"]
272        options["unit"] = options["u"] or options["unit"] or defaults["unit"]
273        options["output"] = options["o"] or options["output"] or defaults["output"]
274        options["pagesize"] = options["p"] or options["pagesize"] or defaults["pagesize"]
275        options["number"] = options["n"] or options["number"] or defaults["number"]
276        options["logo"] = options["l"] or options["logo"]
277        if options["logo"] is None : # Allows --logo="" to disable the logo entirely
278            options["logo"] = defaults["logo"] 
279       
280        if options["help"] :
281            invoiceGenerator.display_usage_and_quit()
282        elif options["version"] :
283            invoiceGenerator.display_version_and_quit()
284        else :
285            retcode = invoiceGenerator.main(args, options)
286    except KeyboardInterrupt :       
287        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
288        retcode = -3
289    except PyKotaCommandLineError, msg :     
290        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
291        retcode = -2
292    except SystemExit :       
293        pass
294    except :
295        try :
296            invoiceGenerator.crashed("pkinvoice failed")
297        except :   
298            crashed("pkinvoice failed")
299        retcode = -1
300
301    try :
302        invoiceGenerator.storage.close()
303    except (TypeError, NameError, AttributeError) :   
304        pass
305       
306    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.