CoreBOSBB
Using the standard 'create invoice from salesorder' in workflows - Printable Version

+- CoreBOSBB (https://discussions.corebos.org)
+-- Forum: Development (https://discussions.corebos.org/forum-18.html)
+--- Forum: coreBOS Development (https://discussions.corebos.org/forum-4.html)
+--- Thread: Using the standard 'create invoice from salesorder' in workflows (/thread-338.html)

Pages: 1 2 3


Using the standard 'create invoice from salesorder' in workflows - Guido1982 - 08-05-2016

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?


RE: Using the standard 'create invoice from salesorder' in workflows - joebordes - 08-05-2016

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.


RE: Using the standard 'create invoice from salesorder' in workflows - Guido1982 - 08-05-2016

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?


RE: Using the standard 'create invoice from salesorder' in workflows - Guido1982 - 08-05-2016

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($result0'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($result0'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.


RE: Using the standard 'create invoice from salesorder' in workflows - Guido1982 - 08-05-2016

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.


RE: Using the standard 'create invoice from salesorder' in workflows - Guido1982 - 08-05-2016

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.


RE: Using the standard 'create invoice from salesorder' in workflows - joebordes - 08-07-2016

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


RE: Using the standard 'create invoice from salesorder' in workflows - joebordes - 08-07-2016

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....


RE: Using the standard 'create invoice from salesorder' in workflows - joebordes - 08-07-2016

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']; 

??


RE: Using the standard 'create invoice from salesorder' in workflows - joebordes - 08-07-2016

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