We are looking for a possibility of sending quotes and invoices with different layouts for different clients in WHMCS. Practically we want to have two brands that coexist in WHMCS and generate quotes and invoices with different logo and address for different clients. This way if a client is part of a specific brand, the quote and invoice that he will receive will be regarding the specific brand.
We were thinking on editing the quotepdf.tpl file to define the client based on the client group. if a client is part of a group, we send a specific quote and invoice, otherwise we send a different one.
The part regarding our task is this one below. Do you think this is a correct way to go? If yes, how can we check the group of which the client is a member every time an invoice or quote is generated and decide which layout to use?
Thank you
# Logo
if (file_exists(ROOTDIR.'/assets/img/logo.png')) $pdf->Image(ROOTDIR.'/assets/img/logo.png', 20, 25, 75);
elseif (file_exists(ROOTDIR.'/assets/img/logo.jpg')) $pdf->Image(ROOTDIR.'/assets/img/logo.jpg', 20, 25, 75);
else $pdf->Image(ROOTDIR.'/assets/img/placeholder.png', 20, 25, 75);
# Company Details
$pdf->SetFont($pdfFont,'',13);
$pdf->Cell(0,6,trim($companyaddress[0]),0,1,'R');
$pdf->SetFont($pdfFont,'',9);
for ( $i = 1; $i <= ((count($companyaddress)>6) ? count($companyaddress) : 6); $i += 1) {
$pdf->Cell(0,4,trim($companyaddress[$i]),0,1,'R');
}
Create different invoice template for each client group, copy the default template into it, and customize.
For group id: 2, create invoicepdf_2.tpl,
For group id: 3, create invoicepdf_3.tpl,
For no group: create invoicepdf_0.tpl
In invoicepdf.tpl, delete all lines and add the following:
<?php
$invoiceFile = __DIR__ . '/invoicepdf_' . $clientsdetails['groupid'] . '.tpl';
if (file_exists($invoiceFile)) {
include $invoiceFile;
}
This way you can customize each file without adding many conditions and checks.
Related
I've got a WooCommerce set up where I currently need to do two things. The first is to move data that is currently in the post.excerpt field to an ACF field that I've created specifically for the purpose while the second is to update the post.excerpt field with new data. All the product data is in SQL-Server Express because the product data came from another website that we're replacing with the WooCommerce one. I exported all the Woocommerce products with basic info like the product ID, SKU, Title and Post_Content and wrote a query in SQL-Server to match the two together. That's been exported as a flat file and imported into MySQL. Now I've writen a query to update the post.excerpt field but what I can't find is a way to update the ACF field in the same query (or another one).
set
'wp.posts'.'post.excerpt' = 'updatelist'.'excerpt'
From 'updatelist'
where
'wp_posts'.'ID' = 'updatelist'.'product_id'
Can anyone help? Please don't suggesting importing a CSV file. There's 180,000 products and using a csv, well it's about 10% of the way through and has taken, so far, 24 hours.
To update ACF fields, first i would usually prepare an aray of key-value pairs of ACF fields to loop over and update them:
# first prepare your array of ACF fields you need to update
acf_fields = [
'field_5f70*********' => 'product_name',
'field_5f80*********' => 'product_color',
'field_5f90*********' => 'product_price'
];
# to find the key values for your own ACF fields, just go to admin dashboard under custom fields, select your group of ACF fields and then on the "Edit Field Group" you see those keys. If you don't see them, choose "screen options" and select "fields keys".
# Now we're going to loop over them and update each field
foreach(acf_fields as $key => $name){
update_field(a, b, c);
# a = $key
# b = value to be updated which comes from your old list (from querying your list)
# c = which post it belongs to (custom query for your custom post type that contains ACF fields)
};
That's how i update my ACF fields, there are other methods using Wordpress REST API too.
I'm trying to create an invoice line using the Web APIs and XML-RPC (python). I receive this error while creating it.
xmlrpc.client.Fault: <Fault 2: 'Cannot create unbalanced journal entry. Ids: [12083]\nDifferences debit - credit: [-2629.33]'>
I'm creating the invoice line as follows:
inv_line_id = models.execute_kw(
db, uid, password,
'account.move.line',
'create'[
{'move_id':invoice_id,
'product_id':product_id[0]['id'],
'price_unit':product_id[0]['list_price'],
'quantity':sale_line_id[0]['product_uom_qty'],
'account_id':account_id[0]['property_account_income_categ_id'][0]
}
]
)
If i dont add the 'price_unit' the invoice line is being created normally but without a price.
Anyone knows how to fix this ? Thanks in advance
If you are in the same version of me (V13) or if the code is the same in your version. You could pass a value in the context to don't check the balanced.
'check_move_validity' = False
Find in the file account/models/account_move.py . Line 1721 in method write
# Ensure the move is still well balanced.
if 'line_ids' in vals:
if self._context.get('check_move_validity', True):
self._check_balanced()
self.update_lines_tax_exigibility()
During the create or write of an account.move with only invoice_line_ids set and not line_ids, the _move_autocomplete_invoice_lines_create or _move_autocomplete_invoice_lines_write method is called to auto compute accounting lines of the invoice. In that case, accounts will be retrieved and taxes, cash rounding, and payment terms will be computed. In the end, the values will contain all accounting lines in line_ids and the moves should be balanced.
Latest version of Odoo 13 dated December 10, 2021.
Edit file: /path_of_odoo/addons/account/models/account_move.py
Example:
vim /odoo/addons/account/models/account_move.py
Modify function named _check_balanced and comment line 1582 and add new line whit return
Example:
############################### Before ##################################
############################### A F T E R ###############################
We have a weekly backup process which exports our production Google Appengine Datastore onto Google Cloud Storage, and then into Google BigQuery. Each week, we create a new dataset named like YYYY_MM_DD that contains a copy of the production tables on that day. Over time, we have collected many datasets, like 2014_05_10, 2014_05_17, etc. I want to create a data set Latest_Production_Data that contains a view for each of the tables in the most recent YYYY_MM_DD dataset. This will make it easier for downstream reports to write their query once and always retrieve the most recent data.
To do this, I have code that gets the most recent dataset and the names of all the tables that dataset contains from the BigQuery API. Then, for each of these tables, I fire a tables.insert call to create a view that is a SELECT * from the table I am looking to create a reference to.
This fails for tables that contain a RECORD field, from what looks to be a pretty benign column-naming rule.
For example, I have this table:
For which I issue this API call:
{
'tableReference': {
'projectId': 'redacted',
'tableId': u'AccountDeletionRequest',
'datasetId': 'Latest_Production_Data'
}
'view': {
'query': u'SELECT * FROM [2014_05_17.AccountDeletionRequest]'
},
}
This results in the following error:
HttpError: https://www.googleapis.com/bigquery/v2/projects//datasets/Latest_Production_Data/tables?alt=json returned "Invalid field name "__key__.namespace". Fields must contain only letters, numbers, and underscores, start with a letter or underscore, and be at most 128 characters long.">
When I execute this query in the BigQuery web console, the columns are renamed to translate the . to an _. I kind of expected the same thing to happen when I issued the create view API call.
Is there an easy way I can programmatically create a view for each of the tables in my dataset, regardless of their underlying schema? The problem I'm encountering now is for record columns, but another problem I anticipate is for tables that have repeated fields. Is there some magic alternative to SELECT * that will take care of all these intricacies for me?
Another idea I had was doing a table copy, but I would prefer not to duplicate the data if I can at all avoid it.
Here is the workaround code I wrote to dynamically generate a SELECT statement for each of the tables:
def get_leaf_column_selectors(dataset, table):
schema = table_service.get(
projectId=BQ_PROJECT_ID,
datasetId=dataset,
tableId=table
).execute()['schema']
return ",\n".join([
_get_leaf_selectors("", top_field)
for top_field in schema["fields"]
])
def _get_leaf_selectors(prefix, field):
if prefix:
format = prefix + ".%s"
else:
format = "%s"
if 'fields' not in field:
# Base case
actual_name = format % field["name"]
safe_name = actual_name.replace(".", "_")
return "%s as %s" % (actual_name, safe_name)
else:
# Recursive case
return ",\n".join([
_get_leaf_selectors(format % field["name"], sub_field)
for sub_field in field["fields"]
])
We had a bug where you needed to need to select out the individual fields in the view and use an 'as' to rename the fields to something legal (i.e they don't have '.' in the name).
The bug is now fixed, so you shouldn't see this issue any more. Please ping this thread or start a new question if you see it again.
We have many accounts we manage and the ones we are interested have the label 'Active Clients'. This label is shown on the first page of the MCC ('My Client Center') in the table. It is the second column of the table that shows 'Labels'.
Here's the basic script:
function main() {
var accountSelector = MccApp.accounts()
.withCondition("Labels = 'Active Clients'");
var accountIterator = accountSelector.get();
// Iterate through the list of accounts
while (accountIterator.hasNext()) {
var account = accountIterator.next();
// Select the client account.
MccApp.select(account);
// Operate on client account
Logger.log('Customer ID: ' + account.getCustomerId() );
}
}
This is how I'm trying to iterate through all clients that have the label 'Active Clients'. It doesn't work and I'm wondering if it can be made to work or I must specify the id's of all clients.
The column name is LabelNames instead of Labels. Also its type is StringSet, not String, so you should use .withCondition("LabelNames CONTAINS_ANY ['Active Clients']"). However, "LabelNames" is not an available column for AccountSelector conditions yet (https://developers.google.com/adwords/scripts/docs/reference/mccapp/mccapp_accountselector#withCondition_1), hope this will change by time.
Anyway, I recommend to create a new MCC, where you keep only active clients, so you can place the script there without using withCondition.
How can i add additional information to the Magento Packingslip PDF. I am using integrated label paper, so i would like to add repeat the customers delivery address at the footer and also, some details like total quantity of items in the order and the cost of the items in the order. I am currently modifying local files in: Mage/Sales/Model/Order/Pdf/ but I have only managed to change to font.
EDIT:
Okay, i have made good progress and added most of the information i need, however, i have now stumbled across a problem.. I would like to get the total weight of all items in each order and Quantity.
I am using this code in the shipment.php:
Under the foreach (This is useful in case an order has a split delivery - as you can have one order with multiple "shipments" So this is why I have the code after here:
foreach ($shipment->getAllItems() as $item){
if ($item->getOrderItem()->getParentItem()) {
continue;
...
then I have this further down:
$shippingweight=0;
$shippingweight= $item->getWeight()*$item->getQty();
$page->drawText($shippingweight . Mage::helper('sales'), $x, $y, 'UTF-8');
This is great for Row totals. But not for the whole shipment. What I need to do is have this bit of code "added up" for each item in the shipment to create the total Weight of the whole shipment. It is very important that I only have the total of the shipment - not the order as I will be splitting shipments.
Almost at the end of getPdf(...) in Mage_Sales_Model_Order_Pdf_Shipment you will find the code that inserts new pages (lines 93-94 in 1.5.0.1)
if($this->y<15)
$page = $this->newPage(....)
happening in a for-loop.
You can change the logic here to make it change page earlier to make room for your extra information and then add it before the page shift. You should also place code after the for-block if you want it to appear on the last page as well.
Note: You should not change files in app/code/code/Mage directly. Instead, you should place your changed files under app/code/local/Mage using the same folder structure. That way your changes won't accidently get overwritten in an upgrade.
This should get you the total number of items in your order (there might be a faster way but don't know it off the top of my head):
$quote = Mage::getModel('sales/quote')->load($order->getQuoteId());
$itemsCount = $quote->getItemsSummaryQty();
For invoice PDF
at app/code/local/Mage/Sales/Model/Order/Pdf/Items/Invoice/Default.php
add this before $lineBlock
$product = Mage::getModel('catalog/product')->loadByAttribute('sku', $this->getSku($item), array('weight'));
$lines[][] = array(
'text' => 'Weight: '. $product->getData('weight')*1 .'Kg/ea. Total: ' .$product->getData('weight')*$item->getQty() . 'Kg',
'feed' => 400
);