Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision ce1ca334

Von Werner Hahn vor mehr als 7 Jahren hinzugefügt

  • ID ce1ca33441d60e07f4107c626c2396baf428d911
  • Vorgänger 55685c0d
  • Nachfolger 13e28674

Shopmodul: Artikel überarbeitet
Dinge aus Connector ausgelagert, die auch für andere Conectoren
gültigkeit haben
Output des Backgroundjob MassUpload überarbeitet
POD
Typo

Unterschiede anzeigen:

SL/BackgroundJob/ShopPartMassUpload.pm
package SL::BackgroundJob::ShopPartMassUpload;
#ShopPartMassUpload
use strict;
use warnings;
......
use SL::DBUtils;
use SL::DB::ShopPart;
use SL::Shop;
use SL::Shop;
use constant WAITING_FOR_EXECUTION => 0;
use constant UPLOAD_TO_WEBSHOP => 1;
......
# Data format:
# my $data = {
# shop_part_record_ids => [ 603, 604, 605],
# num_order_created => 0,
# orders_ids => [1,2,3]
# conversation_errors => [ { id => 603 , item => 2, message => "Out of stock"}, ],
# shop_part_record_ids => [ 603, 604, 605 ],
# todo => $::form->{upload_todo},
# status => SL::BackgroundJob::ShopPartMassUpload->WAITING_FOR_EXECUTION(),
# num_uploaded => 0,
# conversation => [ { id => 603 , number => 2, message => "Ok" or $@ }, ],
# };
sub update_webarticles {
......
my $db = $job_obj->db;
$job_obj->set_data(UPLOAD_TO_WEBSHOP())->save;
my $num_uploaded = 0;
foreach my $shop_part_id (@{ $job_obj->data_as_hash->{shop_part_record_ids} }) {
my $data = $job_obj->data_as_hash;
eval {
my $shop_part = SL::DB::Manager::ShopPart->find_by(id => $shop_part_id);
unless($shop_part){
push @{ $data->{conversion_errors} }, { id => $shop_part_id, number => '', message => 'Shoppart not found' };
push @{ $data->{conversion} }, { id => $shop_part_id, number => '', message => 'Shoppart not found' };
}
my $shop = SL::Shop->new( config => $shop_part->shop );
my $part_hash = $shop_part->part->as_tree;
require SL::JSON;
my $json = SL::JSON::to_json($part_hash);
my $return = $shop->connector->update_part($shop_part, $json, $data->{todo});
my $return = $shop->connector->update_part($shop_part, $data->{todo});
if ( $return == 1 ) {
my $now = DateTime->now;
my $attributes->{last_update} = $now;
$shop_part->assign_attributes(%{ $attributes });
$shop_part->save;
$data->{num_uploaded} = $num_uploaded++;
push @{ $data->{conversion} }, { id => $shop_part_id, number => $shop_part->part->partnumber, message => 'uploaded' };
}else{
push @{ $data->{conversion_errors} }, { id => $shop_part_id, number => '', message => $return };
push @{ $data->{conversion} }, { id => $shop_part_id, number => $shop_part->part->partnumber, message => $return };
}
1;
} or do {
push @{ $data->{conversion_errors} }, { id => $shop_part_id, number => '', message => $@ };
push @{ $data->{conversion} }, { id => $shop_part_id, number => '', message => $@ };
};
$job_obj->update_attributes(data_as_hash => $data);
SL/Controller/ShopPart.pm
require SL::Shop;
my $shop = SL::Shop->new( config => $shop_part->shop );
my $part_hash = $shop_part->part->as_tree;
my $json = SL::JSON::to_json($part_hash);
my $return = $shop->connector->update_part($self->shop_part, $json,'all');
my $return = $shop->connector->update_part($self->shop_part, 'all');
# the connector deals with parsing/result verification, just needs to return success or failure
if ( $return == 1 ) {
......
}
sub action_update {
my ($self) = @_;
$self->create_or_update;
}
sub action_show_price_n_pricesource {
my ($self) = @_;
......
my $online_cat = $online_article->{data}->{categories};
my @cat = ();
for(keys %$online_cat){
# The ShopwareConnector works with the CategoryID @categories[x][0] in others/new Connectors it must be tested
# Each assigned categorie is saved with id,categorie_name an multidimensional array and could be expanded with categoriepath or what is needed
my @cattmp;
push( @cattmp,$online_cat->{$_}->{id} );
push( @cattmp,$online_cat->{$_}->{name} );
push( @cat,\@cattmp );
push @cattmp,$online_cat->{$_}->{id};
push @cattmp,$online_cat->{$_}->{name};
push @cat,\@cattmp;
}
my $attributes->{shop_category} = \@cat;
my $active->{active} = $online_article->{data}->{active};
......
$self->redirect_to( action => 'list_articles' );
}
sub create_or_update {
my ($self) = @_;
my $is_new = !$self->shop_part->id;
# in edit.html all variables start with shop_part
my $params = delete($::form->{shop_part}) || { };
$self->shop_part->assign_attributes(%{ $params });
$self->shop_part->save;
my ( $price, $price_src_str ) = $self->get_price_n_pricesource($self->shop_part->active_price_source);
flash('info', $is_new ? t8('The shop part has been created.') : t8('The shop part has been saved.'));
$self->js->html('#shop_part_description_' . $self->shop_part->id, $self->shop_part->shop_description)
->html('#shop_part_active_' . $self->shop_part->id, $self->shop_part->active)
->html('#price_' . $self->shop_part->id, $::form->format_amount(\%::myconfig,$price,2))
->html('#active_price_source_' . $self->shop_part->id, $price_src_str)
->run('kivi.ShopPart.close_dialog')
->flash('info', t8("Updated shop part"))
->render;
}
sub render_shop_part_edit_dialog {
my ($self) = @_;
# when self->shop_part is called in template, it will be an existing shop_part with id,
# or a new shop_part with only part_id and shop_id set
$self->js
->run(
'kivi.ShopPart.shop_part_dialog',
t8('Shop part'),
$self->render('shop_part/edit', { output => 0 })
)
->reinit_widgets;
$self->js->render;
}
sub action_save_categories {
my ($self) = @_;
my @categories = @{ $::form->{categories} || [] };
# The ShopwareConnector works with the CategoryID @categories[x][0] in others/new Connectors it must be tested
# Each assigned categorie is saved with id,categorie_name an multidimensional array and could be expanded with categoriepath or what is needed
my @cat = ();
foreach my $cat ( @categories) {
my @cattmp;
......
my @shop_parts = @{ $::form->{shop_parts_ids} || [] };
my $job = SL::DB::BackgroundJob->new(
type => 'once',
active => 1,
package_name => 'ShopPartMassUpload',
)->set_data(
shop_part_record_ids => [ @shop_parts ],
todo => $::form->{upload_todo},
status => SL::BackgroundJob::ShopPartMassUpload->WAITING_FOR_EXECUTION(),
conversation_errors => [ ],
my $job = SL::DB::BackgroundJob->new(
type => 'once',
active => 1,
package_name => 'ShopPartMassUpload',
)->set_data(
shop_part_record_ids => [ @shop_parts ],
todo => $::form->{upload_todo},
status => SL::BackgroundJob::ShopPartMassUpload->WAITING_FOR_EXECUTION(),
conversation => [ ],
num_uploaded => 0,
)->update_next_run_at;
SL::System::TaskServer->new->wake_up;
my $html = $self->render('shop_part/_transfer_status', { output => 0 }, job => $job);
my $html = $self->render('shop_part/_upload_status', { output => 0 }, job => $job);
$self->js
->html('#status_mass_upload', $html)
......
->render;
}
sub action_update {
my ($self) = @_;
$self->create_or_update;
}
sub render_shop_part_edit_dialog {
my ($self) = @_;
$self->js
->run(
'kivi.ShopPart.shop_part_dialog',
t8('Shop part'),
$self->render('shop_part/edit', { output => 0 })
)
->reinit_widgets;
$self->js->render;
}
sub create_or_update {
my ($self) = @_;
my $is_new = !$self->shop_part->id;
my $params = delete($::form->{shop_part}) || { };
$self->shop_part->assign_attributes(%{ $params });
$self->shop_part->save;
my ( $price, $price_src_str ) = $self->get_price_n_pricesource($self->shop_part->active_price_source);
flash('info', $is_new ? t8('The shop part has been created.') : t8('The shop part has been saved.'));
$self->js->html('#shop_part_description_' . $self->shop_part->id, $self->shop_part->shop_description)
->html('#shop_part_active_' . $self->shop_part->id, $self->shop_part->active)
->html('#price_' . $self->shop_part->id, $::form->format_amount(\%::myconfig,$price,2))
->html('#active_price_source_' . $self->shop_part->id, $price_src_str)
->run('kivi.ShopPart.close_dialog')
->flash('info', t8("Updated shop part"))
->render;
}
#
# internal stuff
#
......
sub load_pricesources {
my ($self) = @_;
# the price sources to use for the article: sellprice, lastcost,
# listprice, or one of the pricegroups. It overwrites the default pricesource from the shopconfig.
# TODO: implement valid pricerules for the article
my $pricesources;
push( @{ $pricesources } , { id => "master_data/sellprice", name => t8("Master Data")." - ".t8("Sellprice") },
{ id => "master_data/listprice", name => t8("Master Data")." - ".t8("Listprice") },
{ id => "master_data/lastcost", name => t8("Master Data")." - ".t8("Lastcost") }
{ id => "master_data/lastcost", nam => t8("Master Data")." - ".t8("Lastcost") }
);
my $pricegroups = SL::DB::Manager::Pricegroup->get_all;
foreach my $pg ( @$pricegroups ) {
......
require SL::DB::Part;
my $price;
if ($price_src_str eq "master_data") {
my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id], with_objects => ['prices'],limit => 1)->[0];
my $part = SL::DB::Manager::Part->find_by( id => $self->shop_part->part_id );
$price = $part->$price_src_id;
$price_src_str = $price_src_id;
}else{
my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id], with_objects => ['prices'],limit => 1)->[0];
#my $part = SL::DB::Manager::Part->find_by( id => $self->shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id );
my $pricegrp = SL::DB::Manager::Pricegroup->find_by( id => $price_src_id )->pricegroup;
$price = $part->prices->[0]->price;
$price_src_str = $pricegrp;
......
}
sub init_shops {
# data for drop down filter options
require SL::DB::Shop;
my @shops_dd = [ { title => t8("all") , value =>'' } ];
my $shops = SL::DB::Mangager::Shop->get_all( where => [ obsolete => 0 ] );
my @tmp = map { { title => $_->{description}, value => $_->{id} } } @{ $shops } ;
return @shops_dd;
}
sub init_producers {
# data for drop down filter options
my @producers_dd = [ { title => t8("all") , value =>'' } ];
return @producers_dd;
}
1;
......
=head1 NAME
SL::Controller::ShopPart - Controller for managing ShopParts
SL::Controller::ShopPart - Controller for managing ShopParts
=head1 SYNOPSIS
ShopParts are configured in a tab of the corresponding part.
=head1 FUNCTIONS
ShopParts are configured in a tab of the corresponding part.
=head1 ACTIONS
=over 4
=item C<action_update_shop>
To be called from the "Update" button of the shoppart, for manually syncing/upload one part with its shop. Generates a Calls some ClientJS functions to modifiy original page.
To be called from the "Update" button of the shoppart, for manually syncing/upload one part with its shop. Calls some ClientJS functions to modifiy original page.
=item C<action_show_files>
=item C<action_ajax_delete_file>
=item C<action_get_categories>
=item C<action_show_price_n_pricesource>
=item C<action_show_stock>
=item C<action_get_n_write_categories>
Can be used to sync the categories of a shoppart with the categories from online.
Can be used to sync the categories of a shoppart with the categories from online.
=item C<action_save_categories>
The ShopwareConnector works with the CategoryID @categories[x][0] in others/new Connectors it must be tested
Each assigned categorie is saved with id,categorie_name an multidimensional array and could be expanded with categoriepath or what is needed
=item C<action_reorder>
=item C<action_upload_status>
=item C<action_mass_upload>
=item C<action_update>
=item C<create_or_update>
=item C<render_shop_part_edit_dialog>
when self->shop_part is called in template, it will be an existing shop_part with id,
or a new shop_part with only part_id and shop_id set
=item C<add_javascripts>
=item C<load_pricesources>
the price sources to use for the article: sellprice, lastcost,
listprice, or one of the pricegroups. It overwrites the default pricesource from the shopconfig.
TODO: implement valid pricerules for the article
=item C<get_price_n_pricesource>
=item C<check_auth>
=item C<init_shop_part>
=item C<init_file>
=item C<init_shops>
data for drop down filter options
=back
=head1 TODO
Pricesrules, pricessources aren't fully implemented yet.
CheckAuth
Pricesrules, pricessources aren't fully implemented yet.
=head1 AUTHORS
G. Richardson E<lt>information@kivitendo-premium.deE<gt>
W. Hahn E<lt>wh@futureworldsearch.netE<gt>
G. Richardson E<lt>information@kivitendo-premium.deE<gt>
W. Hahn E<lt>wh@futureworldsearch.netE<gt>
=cut
SL/DB/ShopPart.pm
use strict;
use SL::DBUtils;
use SL::DB::MetaSetup::ShopPart;
use SL::DB::Manager::ShopPart;
use SL::DB::Helper::AttrHTML;
......
__PACKAGE__->meta->initialize;
__PACKAGE__->attr_html('shop_description');
sub get_tax_and_price {
my ( $self ) = @_;
require SL::DB::Part;
my $tax_n_price;
my ( $price_src_str, $price_src_id ) = split(/\//,$self->active_price_source);
my $price;
my $part;
if ($price_src_str eq "master_data") {
$part = SL::DB::Manager::Part->find_by( id => $self->part_id );
$price = $part->$price_src_id;
}else{
$part = SL::DB::Manager::Part->find_by( id => $self->part_id );
$price = $part->prices->[0]->price;
}
my $taxrate;
my $dbh = $::form->get_standard_dbh();
my $b_id = $part->buchungsgruppen_id;
my $t_id = $self->shop->taxzone_id;
my $sql_str = "SELECT a.rate AS taxrate from tax a
WHERE a.taxkey = (SELECT b.taxkey_id
FROM chart b LEFT JOIN taxzone_charts c ON b.id = c.income_accno_id
WHERE c.taxzone_id = $t_id
AND c.buchungsgruppen_id = $b_id)";
my $rate = selectall_hashref_query($::form, $dbh, $sql_str);
$taxrate = @$rate[0]->{taxrate}*100;
$tax_n_price->{price} = $price;
$tax_n_price->{tax} = $taxrate;
return $tax_n_price;
}
sub get_images {
my ( $self ) = @_;
require SL::DB::ShopImage;
my $images = SL::DB::Manager::ShopImage->get_all( where => [ 'files.object_id' => $self->{part_id}, ], with_objects => 'file', sort_by => 'position' );
my @upload_img = ();
foreach my $img (@{ $images }) {
my $file = SL::File->get(id => $img->file->id );
my ($path, $extension) = (split /\./, $file->file_name);
my $content = File::Slurp::read_file($file->get_file);
my $temp ={ ( link => 'data:' . $file->mime_type . ';base64,' . MIME::Base64::encode($content, ""), #$content, # MIME::Base64::encode($content),
description => $img->file->title,
position => $img->position,
extension => $extension,
path => $path,
)} ;
push( @upload_img, $temp);
}
return @upload_img;
}
1;
__END__
=pod
=encoding utf-8
=head1 NAME
SL::DB::ShopPart - Model for the 'shop_parts' table
=head1 SYNOPSIS
This is a standard Rose::DB::Object based model and can be used as one.
=head1 METHODS
=over 4
=item C<get_tax_and_price>
Returns the price and the taxrate for an shop_article
=item C<get_images>
Returns the images for the shop_article
=back
=head1 TODO
Prices, pricesources, pricerules could be implemented
=head1 AUTHORS
Werner Hahn E<lt>wh@futureworldsearch.netE<gt>
=cut
SL/ShopConnector/Shopware.pm
}
sub update_part {
my ($self, $shop_part, $json, $todo) = @_;
my ($self, $shop_part, $todo) = @_;
#shop_part is passed as a param
die unless ref($shop_part) eq 'SL::DB::ShopPart';
......
my $url = $self->url;
my $part = SL::DB::Part->new(id => $shop_part->{part_id})->load;
# TODO: Prices (pricerules, pricegroups, multiple prices)
# CVARS to map
my $cvars = { map { ($_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }) } @{ $part->cvars_by_config } };
my @cat = ();
......
push ( @cat, $temp );
}
my $images = SL::DB::Manager::ShopImage->get_all( where => [ 'files.object_id' => $part->{id}, ], with_objects => 'file', sort_by => 'position' );
my @upload_img = ();
foreach my $img (@{ $images }) {
my $file = SL::File->get(id => $img->file->id );
my ($path, $extension) = (split /\./, $file->file_name);
my $content = File::Slurp::read_file($file->get_file);
my $temp ={ ( link => 'data:' . $file->mime_type . ';base64,' . MIME::Base64::encode($content, ""), #$content, # MIME::Base64::encode($content),
description => $img->file->title,
position => $img->position,
extension => $extension,
path => $path,
)} ;
push( @upload_img, $temp);
}
my ($import,$data,$data_json);
# if( $shop_part->last_update){
my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
# Shopware RestApi schreibt Fehleremail wenn Artikel nicht gefunden. es braucht aber irgendeine Abfrage, ob der Artikel schon im Shop ist.
# LWP->post = neuanlegen LWP->put = update
$data = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
$data_json = $data->content;
$import = SL::JSON::decode_json($data_json);
# }
# get the right price
# TODO In extra Helper??
my ( $price_src_str, $price_src_id ) = split(/\//,$shop_part->active_price_source);
require SL::DB::Part;
my $price;
if ($price_src_str eq "master_data") {
my $part = SL::DB::Manager::Part->get_all( where => [id => $shop_part->part_id], with_objects => ['prices'],limit => 1)->[0];
$price = $part->$price_src_id;
}else{
my $part = SL::DB::Manager::Part->get_all( where => [id => $shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id], with_objects => ['prices'],limit => 1)->[0];
$price = $part->prices->[0]->price;
}
# get the right taxrate for the article
# TODO In extra Helper??
my $taxrate;
my $dbh = $::form->get_standard_dbh();
my $b_id = $part->buchungsgruppen_id;
my $t_id = $shop_part->shop->taxzone_id;
my $sql_str = "SELECT a.rate AS taxrate from tax a
WHERE a.taxkey = (SELECT b.taxkey_id
FROM chart b LEFT JOIN taxzone_charts c ON b.id = c.income_accno_id
WHERE c.taxzone_id = $t_id
AND c.buchungsgruppen_id = $b_id)";
my $rate = selectall_hashref_query($::form, $dbh, $sql_str);
$taxrate = @$rate[0]->{taxrate}*100;
my @upload_img = $shop_part->get_images;
my $tax_n_price = $shop_part->get_tax_and_price;
my $price = $tax_n_price->{price};
my $taxrate = $tax_n_price->{tax};
# mapping to shopware still missing attributes,metatags
my %shop_data;
......
my $upload_content;
my $upload;
my ($import,$data,$data_json);
my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
# Shopware RestApi sends an erroremail if configured and part not found. But it needs this info to decide if update or create a new article
# LWP->post = create LWP->put = update
$data = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
$data_json = $data->content;
$import = SL::JSON::decode_json($data_json);
if($import->{success}){
#update
my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
......
# don't know if this is needed
if(@upload_img) {
my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
my $imgup = $self->connector->put($url . "api/generatearticleimages/$partnumber?usenumberasid=true");
my $imgup = $self->connector->put($url . "api/generatearticleimages/$partnumber?useNumberAsId=true");
}
return $upload_content->{success};
......
my $url = $self->url;
$partnumber = $::form->escape($partnumber);#shopware don't accept / in articlenumber
my $data = $self->connector->get($url . "api/articles/$partnumber?usenumberasid=true");
my $data = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
my $data_json = $data->content;
return SL::JSON::decode_json($data_json);
}
js/kivi.ShopPart.js
namespace('kivi.ShopPart', function(ns) {
var $dialog;
// this is called by sub render, with a certain prerendered html (edit.html,categories.html)
ns.shop_part_dialog = function(title, html) {
var id = 'jqueryui_popup_dialog';
var dialog_params = {
......
$dialog.dialog("close");
}
// save existing shop_part_id with new params from form, calls create_or_update and saves to db
ns.save_shop_part = function(shop_part_id) {
var form = $('form').serializeArray();
form.push( { name: 'action', value: 'ShopPart/update' }
......
});
}
// add part to a shop
ns.add_shop_part = function(part_id,shop_id) {
var form = $('form').serializeArray();
form.push( { name: 'action', value: 'ShopPart/update' }
......
});
}
// this is called from tabs/_shop.html, opens edit_window (render)
ns.edit_shop_part = function(shop_part_id) {
$.post('controller.pl', { action: 'ShopPart/create_or_edit_popup', shop_part_id: shop_part_id }, function(data) {
kivi.eval_json_result(data);
});
}
// does the same as edit_shop_part (existing), but with part_id and shop_id, opens edit window (render)
ns.create_shop_part = function(part_id, shop_id) {
$.post('controller.pl', { action: 'ShopPart/create_or_edit_popup', part_id: part_id, shop_id: shop_id }, function(data) {
kivi.eval_json_result(data);
});
}
// gets all categories from the webshop
ns.get_all_categories = function(shop_part_id) {
//var form = new Array; //$('form').serializeArray();
//form.push( { name: 'action', value: 'ShopPart/get_categories' }
// , { name: 'shop_part_id', value: shop_part_id }
//);
$.post('controller.pl', { action: 'ShopPart/get_categories', shop_part_id: shop_part_id }, function(data) {
kivi.eval_json_result(data);
});
//$.post('controller.pl', form, function(data) {
// kivi.eval_json_result(data);
//});
}
// write categories in kivi DB not in the shops DB TODO: create new categories in the shops db
ns.save_categories = function(shop_part_id, shop_id) {
var form = $('form').serializeArray();
form.push( { name: 'action', value: 'ShopPart/save_categories' }
......
$('#shop_images').load(url);
}
//shows the Name and price in _shop.html. Pricerules not implemented yet, just master_data and pricegroups
ns.update_price_n_price_source = function(shop_part_id,price_source) {
$.post('controller.pl', { action: 'ShopPart/show_price_n_pricesource', shop_part_id: shop_part_id, pricesource: price_source }, function(data) {
kivi.eval_json_result(data);
});
}
//shows the local and the online stock
ns.update_stock = function(shop_part_id) {
$.post('controller.pl', { action: 'ShopPart/show_stock', shop_part_id: shop_part_id }, function(data) {
kivi.eval_json_result(data);
templates/webpages/shop_part/_list_articles.html
</td>
<td style="vertical-align:middle;text-align:center;">
[% IF shop_part.active %]
<div id="toogle_[% shop_part.id %]" style="background-image:url(image/gruener_punkt.gif);background-repeat:no-repeat;witdh:15px;height:15px;" onclick="kivi.ShopPart.part_toggle_active([% shop_part.id %] ,[% shop_part.active %]);" onMouseOver="this.style.cursor='pointer'">&nbsp; </div>
<div id="toogle_[% shop_part.id %]" style="background-image:url(image/gruener_punkt.gif);background-repeat:no-repeat;witdh:15px;height:15px;">&nbsp; </div>
[% ELSE %]
<div id="toogle_[% shop_part.id %]" style="background-image:url(image/roter_punkt.gif);background-repeat:no-repeat;witdh:15px;height:15px;" onclick="kivi.ShopPart.part_toggle_active([% shop_part.id %] ,[% shop_part.active %]);" onMouseOver="this.style.cursor='pointer'">&nbsp; </div>
<div id="toogle_[% shop_part.id %]" style="background-image:url(image/roter_punkt.gif);background-repeat:no-repeat;witdh:15px;height:15px;">&nbsp; </div>
[% END %]
</td>
<td>[% L.html_tag('span',LxERP.t8(), id => 'active_price_source_' _ shop_part.id) %] </td>
templates/webpages/shop_part/_transfer_status.html
[% IF !data.status %]
[% LxERP.t8("waiting for job to be started") %]
[% ELSIF data.status == 1 %]
[% LxERP.t8("Creating orders") %]
[% LxERP.t8("Uploading Data") %]
[% ELSE %]
[% LxERP.t8("Done.") %]
[% END %]
</td>
</tr>
<tr>
<th valign="top" align="left">[% LxERP.t8("Number of orders created:") %]</th>
<td valign="top">[% IF data.status > 0 %][% HTML.escape(data.num_created) %] / [% HTML.escape(data.record_ids.size) %][% ELSE %]–[% END %]</td>
<th valign="top" align="left">[% LxERP.t8("Number of data uploaded:") %]</th>
<td valign="top">[% IF data.status > 0 %][% HTML.escape(data.num_uploaded) %] / [% HTML.escape(data.record_ids.size) %][% ELSE %]–[% END %]</td>
</tr>
<tr>
<th valign="top" align="left">[% LxERP.t8("Errors during conversion:") %]</th>
<th valign="top" align="left">[% LxERP.t8("Conversion:") %]</th>
<td valign="top">
[% IF !data.status %]
[% ELSIF !data.conversion_errors.size %]
[% LxERP.t8("No errors have occurred.") %]
[% ELSE %]
<table>
<tr class="listheader">
<th>[% LxERP.t8("Shop Order") %]</th>
<th>[% LxERP.t8("Error") %]</th>
<th>[% LxERP.t8("Part") %]</th>
<th>[% LxERP.t8("Partnumber") %]</th>
<th>[% LxERP.t8("Message") %]</th>
</tr>
[% FOREACH error = data.conversion_errors %]
[% FOREACH message = data.conversion %]
<tr>
<td valign="top">[% IF error.id %][% L.link(SELF.url_for(controller='ShopOrder', action='show', id=error.id), HTML.escape(error.number), target="_blank") %][% ELSE %]–[% END %]</td>
<td valign="top">[% HTML.escape(error.message) %]</td>
<td valign="top">[% HTML.escape(message.message) %]</td>
</tr>
[% END %]
</table>
templates/webpages/shop_part/_upload_status.html
[%- USE Dumper -%]
[% SET data = job.data_as_hash %]
<h2>[% LxERP.t8("Watch status") %]</h2>
[% L.hidden_tag('', job.id, id="smu_job_id") %]
......
[% IF !data.status %]
[% LxERP.t8("waiting for job to be started") %]
[% ELSIF data.status == 1 %]
[% LxERP.t8("Creating orders") %]
[% LxERP.t8("Uploading Data") %]
[% ELSE %]
[% LxERP.t8("Done.") %]
[% END %]
</td>
</tr>
<tr>
<th valign="top" align="left">[% LxERP.t8("Number of orders created:") %]</th>
<td valign="top">[% IF data.status > 0 %][% HTML.escape(data.num_created) %] / [% HTML.escape(data.record_ids.size) %][% ELSE %]–[% END %]</td>
<th valign="top" align="left">[% LxERP.t8("Number of data uploaded:") %]</th>
<td valign="top">[% IF data.status > 0 %][% HTML.escape(data.num_uploaded) %] / [% HTML.escape(data.shop_part_record_ids.size) %][% ELSE %]–[% END %]</td>
</tr>
<tr>
<th valign="top" align="left">[% LxERP.t8("Errors during conversion:") %]</th>
<th valign="top" align="left">[% LxERP.t8("Conversion:") %]</th>
<td valign="top">
[% IF !data.status %]
[% ELSIF !data.conversion_errors.size %]
[% LxERP.t8("No errors have occurred.") %]
[% ELSE %]
<table>
<tr class="listheader">
<th>[% LxERP.t8("Shop Order") %]</th>
<th>[% LxERP.t8("Error") %]</th>
<th>[% LxERP.t8("Part") %]</th>
<th>[% LxERP.t8("Partnumber") %]</th>
<th>[% LxERP.t8("Message") %]</th>
</tr>
[% FOREACH error = data.conversion_errors %]
[% FOREACH message = data.conversion %]
<tr>
<td valign="top">[% IF error.id %][% L.link(SELF.url_for(controller='ShopOrder', action='show', id=error.id), HTML.escape(error.number), target="_blank") %][% ELSE %]–[% END %]</td>
<td valign="top">[% HTML.escape(error.message) %]</td>
<td valign="top">[% HTML.escape(message.id) %]</td>
<td valign="top">[% HTML.escape(message.number) %]</td>
<td valign="top">[% HTML.escape(message.message) %]</td>
</tr>
[% END %]
</table>
[% END %]
</table>
</p>

Auch abrufbar als: Unified diff