Closed (won't fix)
Project:
Invoice
Version:
6.x-1.x-dev
Component:
Documentation
Priority:
Normal
Category:
Feature request
Assigned:
Reporter:
Created:
21 Aug 2009 at 12:37 UTC
Updated:
8 Feb 2013 at 14:03 UTC
Jump to comment: Most recent file
Comments
Comment #1
pietervogelaar commentedThere aren't yet, but I'm planning to create indeed a function like invoice_insert($node) or something. I will build that sometime in the future.
The best you can do for now is study the invoice_ database tables, then you'll see it is very easy to create invoices programmatically with custom invoice inserts.
Greetings,
Pieter
Comment #2
pietervogelaar commentedComment #3
klavs commentedI need this as well.
I am working on doing it, using drupal_execute - I have never done this before, and I tried to dump (dpm) the $node in beginning og invoice_node_insert function - but I can't find the products/invoice_items in that $node - so I am definetely missing something :(
I figure an "easy" way of making this easy for people wanting to programmaticly create invoices, would be to have a function in invoice module, which simply took an array of the necessary variables (the ones shown on the form) and then selected the invoice nr., set the "created"/"changed" variables etc. and called drupal_execute - what do you think?
Comment #4
klavs commentedremember one needs:
module_load_include('inc', 'node', 'node.pages');
$result = drupal_execute('invoice_node_form', $values, $new_invoice_node);
for drupal_execute to work in d6 :)
Comment #5
pietervogelaar commentedI will implement this somewhere in the following weeks...
Pieter
Comment #6
klavs commentedI'll gladly help with this, as I am working on a new site for my small webshop, and need this to work, before I can launch.
I'm on irc (freenode) as Fixion_ or on msn klavs@klavsen.net if you want to work together - I can def. use some help/advise getting it to work.
I'm a little thrown by the missing invoice_items in the $node object in invoice_insert - so I need to figure out why that is, so that I can figure out what needs to be done, when I have the array with relevant information.
As I see it, I need to:
->check I have the data I need in the array
->reserve the invoicenr. in the invoice_invoices table (to ensure someone else isn't creating an invoice in the exact same time - and calls drupal_execute with same invoice nr.)
->find out where I get the $invoice_items
->fill out the $node object to send to drupal_execute (after I figure out why there's no invoice_items in the $node in invoice_insert (hook_insert)
->call drupal_execute - and whatever is needed for invoice_items
Comment #7
pietervogelaar commentedIt's built like this:
Every user (which has a unique ID) can create invoices if they have the proper permissions. Sometimes you must insert an invoice with for example 30 lines. It's VERY annoying if you try to save something and it goes wrong and they are gone, or if you browser crashes or anything. So everytime you add an invoice item on a new invoice it is saved in the table invoice_items with invoice_id = 0. This way it is also possible to shutdown you computer and complete the invoice another time or on another computer.
When the actual insert is done, the invoice will be created and will get an invoice ID. On that moment all the invoice items that have invoice_id = 0 will be updated with the invoice ID of the newly created invoice.
The easiest way of creating an invoice programmatically is just create a function that does the database inserts and takes a big array with info about the invoice and its items. So don't use drupal_execute() or something at all.
Pieter
Comment #8
klavs commentedso wouldn't the best way be, to just add a few checks to see if invoice_number and title is set - and if not - just set those, using the same functions as done in invoice_nodeapi ?
This would add only a few lines to function - and to call from other modules - you'd just have to load the invoice module - and call invoice_insert (which seems like a good name as well :)
Comment #9
klavs commentedor perhaps to be "absolutely sure" - one could add a magic $node->createit variable - which would need to be set to "yes" ?
Comment #10
klavs commentedohh - and ofcourse there needs to be an $invoice_items array - so you'd run those invoice_item inserts (as usually done from invoice_ajax.inc) when that array is there.
The existence of the $invoice_items array could even be the "magic marker" to do the aforementioned extras - before continueing with invoice_insert as usual.
Comment #11
klavs commentedThis doesn't protect against race conditions (between getting the next invoice number from your helper function, until finishing off invoice_insert).
IMHO invoice_insert needs a:
db_lock_table("invoice_invoices");
db_lock_table("invoice_items");
and then a
db_unlock_tables();
at the end of invoice_insert.
Comment #12
pietervogelaar commentedWhy?
You can just do an insert in the invoice_invoices table. Get the invoice id, and insert invoice items in the invoice_items table??
Pieter
Comment #13
klavs commentedwell - as I understand the use you make of the _invoice_get_new_invoice_number function - you call it sometimes, without actually wanting to use the number it returns.
Between a function gets this number back - and it actually inserts something in the invoice_invoices - another function might just call the function as well - and if they both end up using the number - there will be two invoices with same iid - since the iid row is not set as a primary key (and thus not unique AFAIK).
Since the _invoice_get_new_invoice_number function can't know if the invoice number is going to be used - it can't reserve it so I don't really see a good solution to this problem - and I figure it would be hard to hit, unless I did something stupid :)
What do you think about the idea of adding a small segment to invoice_insert - and then having that be the function for external modules to use, for creating invoices as well?
Comment #14
pietervogelaar commentedI don't have time now, but I will dive into the code soon..
Pieter
Comment #15
klavs commentedI just noticed that invoice_insert doesn't create the $node in the node tables -
It seems the best way to do this would just be to:
define a new invoice_save function (or whatever):
which would:
insert invoice_items with the correct iid (or with 0 as iid - doesn't make a difference in this case unfortunately)
call: node_save($node);
node_save then invokes invoice_insert as usual.
That would mean that if someone is manually creating an invoice_item (using create/node/invoice or what the url is) - they will be added to the invoice created by invoice_save - as they all have iid 0.
a fix could be to add a "magic marker" to the $node object - and check for that, before adding invoice_items with iid=0 to the invoice being created.
This is beginning to sound sane - so I'll try to implement it and see how it goes :) - if it actually works - I'll submit a patch here.
Thank you for your very valuable feedback.
Comment #16
klavs commentedHere's a patch for an invoice_save function that works like a charm for me.
Here's what the attached patch adds/changes/needs:
- Adds functions: invoice_save(&$node) and _invoice_for_order_exists($order_id)
- Needs an int(10) column for order_id in invoice_invoices - as an invoice is usually (in webshops it's always) the result of an order. This also means I can test if there's already an invoice for a given order - so as to not accidentally create two for the same order (thus incurring double taxes and wrong accounting information etc.)
- I removed the check for the company being the same - as I have several different people from the same company, that order seperately so I see no logic in doing such an assumption.
I use this functions from ubercart, and it works like a charm, and customers can now view their invoices (and download PDF version) from their order history - and invoices is generated for an order, by a single click :)
Comment #17
Max_Headroom commentedHere is some example of code I used to create an invoice programmatially going the node_save route.
It uses the default template and saves 1 item.
Can easily be modified for more.
Comment #18
klavs commentedwas my code of any use? did you have to alter anything?
Comment #19
Max_Headroom commentedNo, I did not use your code, as I needed to create the node (invoice) programmatically based on other information.
Comment #20
klavs commentedI create invoices programmaticly - using the invoice_save function (which is a hook - called when the call to node_save is executed).
How does your invoice_save function look like?
Comment #21
Max_Headroom commentedThat's it. I changed nothing else.
The nodeapi and invoice_save in invoice module does the rest.
Comment #22
klavs commentedSo you did add my patch to your invoice module - before writing your example.
Invoice module currently has no invoice_save function (that's why I wrote it).
Comment #23
Max_Headroom commentedSorry, I meant invoice_insert (hook_insert).
Comment #24
klavs commentedNow that makes sense.. I confused myself as well :)
I wanted to avoid having to mess with the tables that belong to the invoice module - from another module.
That's why I added invoice_save - which does the db_query calls that you do yourself in your example - and this also pushes the automated invoicenumber assignment handling to the invoice module for handling. This way - no matter what is done outside of invoice module - the invoice module can ensure valid invoices (ie. numeric ordered invoice numbers WITHOUT holes - as is the rule in EU atleast :)
Comment #25
cbrompton commentedHas anyone had any luck with this?
I also am in need of this sort of feature. I would like an invoice created programmatically.
I would like an invoice created everytime I create content of x-type. the information for the invoice would be taken from the cck fields and taxonomy of the node created. any insight on this would be awesome! I tried using rules and have had no luck
thanks
Comment #26
klavs commentedas I said in #16 - I use it - with the patch in #16 applied. I've written my own module - which simply gives me a list of orders in certain states - and then I click of the ones I want an invoice generated for - and it loops through them, building the $node object and calling invoice_save for function for each (which uses std. node_save function as you can plainly see).
Comment #27
mandreato commented@klavs could you share the custom module you created to integrate Invoice with Ubercart ?
Thank You In Advance.
Comment #28
klavs commented@mandreato: the module simply lists orders of a certain state in a table - with a checkbox and several submit buttons (using basic drupal form/table functionality) to do stuff, such as create invoices by looping over them, and calling the module that fills an object and submits it to invoice_save function (see my patch above). It's very specific for my usage.
The only thing I figure you could use of it, is how I filled the object, before sending it to invoice_save - and that's shown in Max_headrooms post.
Besides that, I set it up, so I can create an invoice from ubercart order view.
I modified ubercart (hack :) - and added an icon (with view order, edit order etc. icons) with a simple link to a path, I coded into an existing module (I was lazy) which simply calls invoice_save - after filling the object as Max_headroom showed.
I don't see what else you need to implement it - and I see no benefit to you from having my module.
Currently ubercart has no hooks to allow this functionality implemented from a module - so if you want to do it as a module, you'd need to ask the ubercart devs to add a hook.
Comment #29
mandreato commentedOK klavs, I'll try to build my own module starting from Max_headrooms example and your useful advices.
Comment #30
surgeonbor commentedThis thread is old, but I'm going through this process now and wanted to add one comment. If you use code like what's in comment #17, the line items won't necessarily remain in the same order in which you create them. To keep the order intact, modify the INSERT statement to include the 'weight' column. In my case, I have a for-loop in which I add one line item per loop, so I use a counter to set the value of 'weight'.
Comment #31
pietervogelaar commentedInvoice module version 6 is not maintained anymore, please upgrade to 7 which has REST API access. So the programmatic operations should be a breeze.