Posts: 855
Threads: 238
Joined: Nov 2015
Reputation:
96
I want to create an invoice from an workflow, but of course I'd like the same behaviour as when you click 'create invoice' in the salesorder detail view. Is there a backend method for this that you can incorporate in a workflow script?
Posts: 3,564
Threads: 36
Joined: Apr 2014
Reputation:
49
No, off the top of my head I don't think there is anything like this.
My recommendation would be to use webservice: call vtws_retrieve to get the SO details, modify the values, look at getConvertSoToInvoice() in EditViewUtils.php and then send in the values to vtws_create.
Let me know how this goes as this is something I have been wanting to do for some time now as a native part of the workflow system, I just haven't been able to get around to it, so if we do this together I will add it to the base project.
Joe
TSolucio
Posts: 855
Threads: 238
Joined: Nov 2015
Reputation:
96
08-05-2016, 08:41 AM
(This post was last modified: 08-05-2016, 08:53 AM by Guido1982.)
I'll get right to it, update soon.
(08-05-2016, 08:30 AM)joebordes Wrote: No, off the top of my head I don't think there is anything like this.
My recommendation would be to use webservice: call vtws_retrieve to get the SO details, modify the values, look at getConvertSoToInvoice() in EditViewUtils.php and then send in the values to vtws_create.
Don't we have the $focus object available, which would eliminate the need to do a vtws_retrieve?
Posts: 855
Threads: 238
Joined: Nov 2015
Reputation:
96
08-05-2016, 10:10 AM
(This post was last modified: 08-05-2016, 10:11 AM by Guido1982.)
Here's my test code up till now:
PHP Code:
<?php
error_reporting(E_ERROR); ini_set("display_errors", "on");
/*=========== Retrieve SO =============*/ // Retrieve SO crmid global $adb; $query = 'SELECT salesorderid FROM vtiger_salesorder WHERE salesorder_no=?'; $params = array('ORD3777'); $result = $adb->pquery($query, $params); $so_crmid = $adb->query_result($result, 0, 'salesorderid');
// Get the webservice object for this SO include_once 'include/Webservices/Retrieve.php'; include_once 'include/Webservices/Create.php'; include_once 'modules/Users/Users.php';
$user = new Users(); $current_user = $user->retrieveCurrentUserInfoFromFile(Users::getActiveAdminId());
try { $wsid = vtws_getWebserviceEntityId('SalesOrder', $so_crmid); $wsso = vtws_retrieve($wsid, $current_user); // echo "<pre>"; // print_r($wsso); // echo "</pre>"; } catch (WebServiceException $ex) { echo $ex->getMessage(); }
// Create an invoice from this SO try { // Get the account NO list($ws_acc_id, $crm_acc_id) = explode('x', $wsso['account_id']); $query = 'SELECT account_no FROM vtiger_account WHERE accountid=?'; $params = array($crm_acc_id); $result = $adb->pquery($query, $params); $acc_no = $adb->query_result($result, 0, 'account_no'); // refactor the SO webservice object for invoice injection if (is_array($wsso)) { $data = $wsso; // Overwrite data that is different for invoices $data['LineItems'] = $wsso['pdoInformation']; $data['invoicedate'] = date('d-m-Y'); $data['salesorder_id'] = $wsso['id']; $data['customerno'] = $acc_no; $data['invoicestatus'] = 'Created'; $data['sostatus'] = 'Created'; $data['exact_payment_cond'] = '3 - Betaling binnen 30 dagen'; } $invoice = vtws_create('Invoice', $data, $current_user); // echo "<pre>"; // print_r($data); // echo "</pre>"; } catch (WebServiceException $ex) { echo $ex->getMessage(); }
I've set a fixed SO number from a test install just to test. Please ignore the exact_payment_cond' field since it is only required if you have this module (which only one installation has). This works, but I think the nicest way would be to create a full workflow task with configuration so you can also set things like the invoice expiration date/number of days in relation to creation date.
Posts: 855
Threads: 238
Joined: Nov 2015
Reputation:
96
08-05-2016, 12:22 PM
(This post was last modified: 08-05-2016, 12:58 PM by Guido1982.)
I've created these files (yes, I should have used one commit in stead of four):
https://github.com/Luke1982/corebos/commit/f380f050b7921d9cef221477a201fcc88c5718de
https://github.com/Luke1982/corebos/commit/38c1a530bb80758ff2082b8b31d0c0819f82ac2a
https://github.com/Luke1982/corebos/commit/eb5ab581456145f04c6b74d969f71d604f1d7364
https://github.com/Luke1982/corebos/commit/23955c89634812a4cc2549defcead0e313694f8e
The update process works. The problem is when I try to create a new workflow using this task, I get the following:
Code:
Fatal error: Declaration of CreateInvFromSO::doTask() must be compatible with VTTask::doTask(&$data) in {$MY_SERVER}/modules/com_vtiger_workflow/tasks/create_invoice_from_so.inc on line 88
Nevermind. I forgot the '&'. Apparently this is a reference function.
Okay, the complete set is as follows:
I've tested this, and it works for me. Also the number of days added to the creation date works. This does not take into account weekdays/business days, it's just days. Please review the code and let me know where it could be better. Please also test this, I've done so but I don't know if there are any caveats left...
Hm, I seem to have found one bug that wasn't there before: the product lines aren't transported to the invoice anymore. This worked in my tests before, but not anymore.
Posts: 855
Threads: 238
Joined: Nov 2015
Reputation:
96
I've tested with my test file again, but still I can manually create an invoice from a salesorder, but I can't get the product lines accross in the actual workflow. Weird thing is that if I copy the example in this file I get a fatal error because 'doTask' does not comply with the parent class or interface. I can solve this by passing in the argument by reference like here but I don't understand why other workflows don't have to do this.
Maybe this has something to do with the productlines not being copied.
Posts: 3,564
Threads: 36
Joined: Apr 2014
Reputation:
49
The missing ampersand has nothing to do with the problem. The correct way to define the task is WITH the ampersand.
When you extend a class
PHP Code:
class CBTagTask extends VTTask{
you inherit all of it's methods, but if that class has defined any method as "abstract" your new class MUST implement those and they must follow the exact same profile as defined in the base class.
You can see here
https://github.com/tsolucio/corebos/blob/master/modules/com_vtiger_workflow/VTTaskManager.inc#L166
that the doTask() method gets the entity by reference so your class, which constructs upon that one has to define that method like that.
i will look into the problem you are having as soon as possible and get back to you
Joe
TSolucio
Posts: 3,564
Threads: 36
Joined: Apr 2014
Reputation:
49
Next up is the default value for the due date. You should not need to setup the workflow task to get the duedate offset. This is better done using (Field) Mapping Business Mapping
http://corebos.org/documentation/doku.php?id=en:adminmanual:businessmappings:mapping
This type of business mapping serves to define field values to capture when converting from one module to another, but it also serves to set default values when creating a new record. You can achieve this by creating a mapping with the same module name for both sides, for example, in this case Invoice2Invoice. Just to test it I created the mapping below and now when I create a new invoice my due date is set to 30 days from today.
Code:
<map>
<originmodule>
<originname>Invoice</originname>
</originmodule>
<targetmodule>
<targetname>Invoice</targetname>
</targetmodule>
<fields>
<field>
<fieldname>duedate</fieldname>
<Orgfields>
<Orgfield>
<OrgfieldName>add_days(get_date('today'), 30)</OrgfieldName>
<OrgfieldID>expression</OrgfieldID>
</Orgfield>
</Orgfields>
</field>
</fields>
</map>
we must get the workflow task to use this mapping because with that you can define ANY field as you like without having to modify the workflow and it will also serve for normal user creation.
give that a try just to get a hang of how incredibly powerful the mappings are and how much you can really customize coreBOS now without touching to much code.
I continue....
Joe
TSolucio
Posts: 3,564
Threads: 36
Joined: Apr 2014
Reputation:
49
Why did you change the name here:
https://github.com/Luke1982/corebos/blob/createInvoiceFromSO_WF/modules/com_vtiger_workflow/tasks/create_invoice_from_so.inc#L71
PHP Code:
$data['LineItems'] = $wsso['pdoInformation'];
shouldn't it be
PHP Code:
$data['pdoInformation'] = $wsso['pdoInformation'];
??
Joe
TSolucio
Posts: 3,564
Threads: 36
Joined: Apr 2014
Reputation:
49
Ok, I had a look. This is what you have to do
https://github.com/tsolucio/corebos/blob/master/modules/Invoice/EditView.php#L57
basically repeat that logic. It will be something in this line:
PHP Code:
$focus = CRMEntity::getInstance('Invoice');
list($so_wsid,$soid) = explode('x',$entity->getId()); $so_focus = CRMEntity::getInstance('SalesOrder'); $so_focus->id = $soid; $so_focus->retrieve_entity_info($soid, "SalesOrder"); $focus = getConvertSoToInvoice($focus, $so_focus, $soid); // this will do the Mapping for the duedate and others SalesOrder2Invoice
// we do this to get the pdoInformation array $wsso = vtws_retrieve($entity->getId(), $current_user); $focus->column_fields['pdoInformation'] = $wsso['pdoInformation'];
$invoice = vtws_create('Invoice', $focus->column_fields, $current_user);
that should be all you need.
Please BE CAREFUL I have NOT tested the code above
Joe
TSolucio
|