map { $form->{$_} = $ref->{$_} } keys %{ $ref };
$query = qq|SELECT b.*, EXISTS
- (SELECT i.warehouse_id
- FROM inventory i
+ (SELECT i.warehouse_id, p.warehouse_id
+ FROM inventory i, parts p
WHERE i.bin_id = b.id
+ OR p.bin_id = b.id
LIMIT 1)
AS in_use
FROM bin b
}
if ($form->{no_services}) {
- $filter .= qq| AND (inventory_accno_id is not NULL or assembly=TRUE)|; # @mb hier nochmal optimieren ... nach kurzer ruecksprache alles i.o.
+ $filter .= qq| AND (inventory_accno_id is not NULL or assembly=TRUE)|;
}
substr($filter, 1, 3) = "WHERE" if ($filter);
$order_dir = $order_dir ? "ASC" : "DESC";
my $query =
- qq|SELECT id, partnumber, description, ean | .
+ qq|SELECT id, partnumber, description, ean, | .
+ qq| warehouse_id, bin_id | .
qq|FROM parts $filter | .
qq|ORDER BY $order_by $order_dir|;
my $sth = $dbh->prepare($query);
ar_show_mark_as_paid => { type => 'boolean', default => 'true' },
ap_show_mark_as_paid => { type => 'boolean', default => 'true' },
assemblynumber => { type => 'text' },
+ warehouse_id => { type => 'integer' },
+ bin_id => { type => 'integer' },
],
primary_key_columns => [ 'id' ],
+
+ foreign_keys => [
+ bin => {
+ class => 'SL::DB::Bin',
+ key_columns => { bin_id => 'id' },
+ },
+
+ warehouse => {
+ class => 'SL::DB::Warehouse',
+ key_columns => { warehouse_id => 'id' },
+ },
+ ],
);
1;
inventory_accno_id => { type => 'integer' },
income_accno_id => { type => 'integer' },
expense_accno_id => { type => 'integer' },
- bin => { type => 'text' },
shop => { type => 'boolean', default => 'false' },
obsolete => { type => 'boolean', default => 'false' },
bom => { type => 'boolean', default => 'false' },
onhand => { type => 'numeric', default => '0', precision => 5, scale => 25 },
stockable => { type => 'boolean', default => 'false' },
has_sernumber => { type => 'boolean', default => 'false' },
+ warehouse_id => { type => 'integer' },
+ bin_id => { type => 'integer' },
],
primary_key_columns => [ 'id' ],
allow_inline_column_values => 1,
foreign_keys => [
+ bin => {
+ class => 'SL::DB::Bin',
+ key_columns => { bin_id => 'id' },
+ },
+
buchungsgruppen => {
class => 'SL::DB::Buchungsgruppe',
key_columns => { buchungsgruppen_id => 'id' },
class => 'SL::DB::Unit',
key_columns => { unit => 'name' },
},
+
+ warehouse => {
+ class => 'SL::DB::Warehouse',
+ key_columns => { warehouse_id => 'id' },
+ },
],
);
$query =
qq|SELECT doi.id AS delivery_order_items_id,
p.partnumber, p.assembly, p.listprice, doi.description, doi.qty,
- doi.sellprice, doi.parts_id AS id, doi.unit, doi.discount, p.bin, p.notes AS partnotes,
+ doi.sellprice, doi.parts_id AS id, doi.unit, doi.discount, p.notes AS partnotes,
doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost,
doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription,
doi.price_factor_id, doi.price_factor, doi.marge_price_factor, doi.pricegroup_id,
notes = ?,
formel = ?,
rop = ?,
- bin = ?,
+ warehouse_id = ?,
+ bin_id = ?,
buchungsgruppen_id = ?,
payment_id = ?,
inventory_accno_id = $subq_inventory,
$form->{notes},
$form->{formel},
$form->{rop},
- $form->{bin},
+ conv_i($form->{warehouse_id}),
+ conv_i($form->{bin_id}),
conv_i($form->{buchungsgruppen_id}),
conv_i($form->{payment_id}),
conv_i($form->{buchungsgruppen_id}),
# retrieve assembly items
my $query =
qq|SELECT p.id, p.partnumber, p.description,
- p.bin, p.onhand, p.rop,
+ p.onhand, p.rop,
(SELECT sum(p2.inventory_accno_id)
FROM parts p2, assembly a
WHERE (p2.id = a.parts_id) AND (a.id = p.id)) AS inventory
# partnumber ean description partsgroup microfiche drawing
#
# column flags:
-# l_partnumber l_description l_listprice l_sellprice l_lastcost l_priceupdate l_weight l_unit l_bin l_rop l_image l_drawing l_microfiche l_partsgroup
+# l_partnumber l_description l_listprice l_sellprice l_lastcost l_priceupdate l_weight l_unit l_rop l_image l_drawing l_microfiche l_partsgroup
#
# exclusives:
# itemstatus = active | onhand | short | obsolete | orphaned
my @apoe_filters = qw(transdate);
my @like_filters = (@simple_filters, @invoice_oi_filters);
my @all_columns = (@simple_filters, @makemodel_filters, @apoe_filters, @project_filters, qw(serialnumber));
- my @simple_l_switches = (@all_columns, qw(notes listprice sellprice lastcost priceupdate weight unit bin rop image));
+ my @simple_l_switches = (@all_columns, qw(notes listprice sellprice lastcost priceupdate weight unit rop image));
my @oe_flags = qw(bought sold onorder ordered rfq quoted);
my @qsooqr_flags = qw(invnumber ordnumber quonumber trans_id name module qty);
my @deliverydate_flags = qw(deliverydate);
my $token_builder = $make_token_builder->(\%joins_needed);
- my @sort_cols = (@simple_filters, qw(id bin priceupdate onhand invnumber ordnumber quonumber name serialnumber soldtotal deliverydate));
+ my @sort_cols = (@simple_filters, qw(id priceupdate onhand invnumber ordnumber quonumber name serialnumber soldtotal deliverydate));
$form->{sort} = 'id' unless grep { $form->{"l_$_"} } grep { $form->{sort} eq $_ } @sort_cols; # sort by id if unknown or invisible column
my $sort_order = ($form->{revers} ? ' DESC' : ' ASC');
my $order_clause = " ORDER BY " . $token_builder->($form->{sort}) . ($form->{revers} ? ' DESC' : ' ASC');
if ($form->{searchitems} eq 'assembly' && $form->{bom}) {
$query =
qq|SELECT p.id, p.partnumber, p.description, a.qty AS onhand,
- p.unit, p.bin, p.notes,
+ p.unit, p.notes,
p.sellprice, p.listprice, p.lastcost,
p.rop, p.weight, p.priceupdate,
p.image, p.drawing, p.microfiche,
$transdate = $form->{deliverydate};
}
} elsif ($form->{script} eq 'ir.pl') {
- # when a purchase invoice is opened from the report of purchase invoices
+ # when a purchase invoice is opened from the report of purchase invoices
# $form->{type} isn't set, but $form->{script} is, not sure why this is or
# whether this distinction matters in some other scenario. Otherwise one
# could probably take out this elsif and add a
i.id AS invoice_id,
i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.parts_id AS id, i.unit, i.deliverydate, i.project_id, i.serialnumber,
i.price_factor_id, i.price_factor, i.marge_price_factor, i.discount,
- p.partnumber, p.inventory_accno_id AS part_inventory_accno_id, p.bin, pr.projectnumber, pg.partsgroup
+ p.partnumber, p.inventory_accno_id AS part_inventory_accno_id, pr.projectnumber, pg.partsgroup
FROM invoice i
JOIN parts p ON (i.parts_id = p.id)
my $query =
qq|SELECT
p.id, p.partnumber, p.description, p.lastcost AS sellprice, p.listprice,
- p.unit, p.assembly, p.bin, p.onhand, p.formel,
+ p.unit, p.assembly, p.onhand, p.formel,
p.notes AS partnotes, p.notes AS longdescription, p.not_discountable,
p.inventory_accno_id, p.price_factor_id,
i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate,
i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost,
i.price_factor_id, i.price_factor, i.marge_price_factor,
- p.partnumber, p.assembly, p.bin, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice,
+ p.partnumber, p.assembly, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice,
pr.projectnumber, pg.partsgroup, prg.pricegroup
FROM invoice i
c3.new_chart_id AS expense_new_chart,
date($transdate) - c3.valid_from AS expense_valid,
- p.unit, p.assembly, p.bin, p.onhand,
+ p.unit, p.assembly, p.onhand,
p.notes AS partnotes, p.notes AS longdescription,
p.not_discountable, p.formel, p.payment_id AS part_payment_id,
p.price_factor_id,
return $self->{data}->{purchase_delivery_order_show_delete};
}
+sub get_default_warehouse_id {
+ my ($self) = @_;
+ return ($self->{data}->{warehouse_id});
+}
+
+sub get_default_bin_id {
+ my ($self) = @_;
+ return ($self->{data}->{bin_id});
+}
+
1;
__END__
Returns the default behavior for showing the delete button for the
corresponding record type (true or false).
+=item C<get_default_warehouse_id>
+
+Returns the default warehouse_id
+
+=item C<get_default_bin_id>
+
+Returns the default bin_id
+
=back
=head1 BUGS
c3.accno AS expense_accno, c3.new_chart_id AS expense_new_chart, date($transdate) - c3.valid_from as expense_valid,
oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
p.partnumber, p.assembly, p.listprice, o.description, o.qty,
- o.sellprice, o.parts_id AS id, o.unit, o.discount, p.bin, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id,
+ o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id,
o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
o.price_factor_id, o.price_factor, o.marge_price_factor,
$form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
'partsgroup' => 'all_partsgroup',
- 'vendors' => 'ALL_VENDORS',);
+ 'vendors' => 'ALL_VENDORS',
+ 'warehouses' => { 'key' => 'WAREHOUSES',
+ 'bins' => 'BINS', });
+ # leerer wert für Lager und Lagerplatz korrekt einstellt
+ # ID 0 sollte in Ordnung sein, da der Zähler sowieso höher ist
+ my $no_default_bin_entry = { 'id' => '0', description => '--', 'BINS' => [ { id => '0', description => ''} ] };
+ push @ { $form->{WAREHOUSES} }, $no_default_bin_entry;
+ if (my $max = scalar @{ $form->{WAREHOUSES} }) {
+
+ my $default_warehouse_id = $::instance_conf->get_default_warehouse_id;
+ my $default_bin_id = $::instance_conf->get_default_bin_id;
+ $form->{warehouse_id} ||= $default_warehouse_id || $form->{WAREHOUSES}->[$max -1]->{id};
+ $form->{bin_id} ||= $default_bin_id || $form->{WAREHOUSES}->[$max -1]->{BINS}->[0]->{id};
+ }
IC->retrieve_buchungsgruppen(\%myconfig, $form);
$form->error($locale->text('Description must not be empty!')) unless $form->{description};
$form->error($locale->text('Partnumber must not be set to empty!')) if $form->{id} && !$form->{partnumber};
+ # undef warehouse_id if the empty value is selected
+ if ( ($form->{warehouse_id} == 0) && ($form->{bin_id} == 0) ) {
+ undef $form->{warehouse_id};
+ undef $form->{bin_id};
+ }
# save part
if (IC->save(\%myconfig, \%$form) == 3) {
$form->error($locale->text('Partnumber not unique!'));
'2. Quarter' => '2. Quartal',
'3. Quarter' => '3. Quartal',
'4. Quarter' => '4. Quartal',
+ '<b> I DO CARE!</b> Please check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' => '<b>ICH KÜMMER MICH</b> Bitte haken Sie Lager und Lagerplätze erzeugen an (Automatisches Zuweisen der Lagerplätze) und vergeben einen Namen für dieses Lager (Lagerplätze werden automatisch übernommen). Danach auf weiter.',
+ '<b> I DO CARE!</b> Please click back and cancel the update and come back after there has been at least one warehouse defined with bin(s).:' => '<b>ICH KÜMMER MICH</b> Brechen Sie das Update ab und legen selber mindestens ein Lager mit Lagerplätzen unter dem Menü System / Lager an.',
+ '<b> I DO NOT CARE</b> Please click continue and the following data (see list) will be deleted:' => '<b>IST MIR EGAL</b> Mit einem Klick auf Weiter (rot) werden keine Daten übernommen, bzw. migriert und die folgende Information in der untenstehenden Liste wird gelöscht.',
+ '<b>Automatically create new bins</b> in the following new warehouse ' => '<b>Automatisches Zuweisen der Lagerplätze</b> im folgenden neuem Lager:',
+ '<b>Automatically create new bins</b> in the following warehouse if not selected in the list above' => '<b>Automatisches Zuweisen der Lagerplätze</b> im folgenden Lager, falls keine andere Zuweisung oben ausgewählt ist. ',
+ '<b>Default Bins Migration !READ CAREFULLY!</b>' => 'Standardlagerplatz Migraition !AUFMERKSAM LESEN!',
'<b>What</b> do you want to look for?' => '<b>Wonach</b> wollen Sie suchen?',
'A Buchungsgruppe consists of a descriptive name and the account numbers for the income and expense accounts for those four tax zones as well as the inventory account number.' => 'Eine Buchungsgruppe besteht aus einem deskriptiven Namen, den Erlös- und Aufwandskonten für diese vier Steuerzonen sowie aus einem Inventarkonto.',
'A digit is required.' => 'Eine Ziffer ist vorgeschrieben.',
'ASSETS' => 'AKTIVA',
'ATTENTION! If you enabled this feature you can not simply turn it off again without taking care that best_before fields are emptied in the database.' => 'ACHTUNG! Wenn Sie diese Einstellung aktivieren, dann können Sie sie später nicht ohne Weiteres deaktivieren, ohne dafür zu sorgen, dass die Felder der Mindeshaltbarkeitsdaten in der Datenbank leer gemacht werden.',
'ATTENTION! You can not simply change it from periodic to perpetual once you started posting.' => 'ACHTUNG! Es kann nicht ohne Weiteres im laufenden Betrieb von der Aufwandsmethode zur Bestandsmethode gewechselt werden.',
+ 'AUTOMATICALLY MATCH BINS' => 'LAGERPLÄTZE AUTOMATISCH ZUWEISEN',
'Abort' => 'Abbrechen',
'Abrechnungsnummer' => 'Abrechnungsnummer',
'Abteilung' => 'Abteilung',
'Billing/shipping address (street)' => 'Rechnungsadresse (Straße)',
'Billing/shipping address (zipcode)' => 'Rechnungsadresse (PLZ)',
'Bin' => 'Lagerplatz',
- 'Bin 2' => '',
'Bin From' => 'Quelllagerplatz',
'Bin List' => 'Lagerliste',
'Bin To' => 'Ziellagerplatz',
'Cash' => 'Zahlungsverkehr',
'Cc' => 'Cc',
'Cc E-mail' => 'CC (E-Mail)',
+ 'Change default bin for this parts' => 'Standardlagerplatz für diese Waren ändern',
'Change kivitendo installation settings (all menu entries beneath \'System\')' => 'Verändern der kivitendo-Installationseinstellungen (Menüpunkte unterhalb von \'System\')',
'Change representative to' => 'Vertreter ändern in',
'Changes in this block are only sensible if the account is NOT a summary account AND there exists one valid taxkey. To select both Receivables and Payables only make sense for Payment / Receipt (i.e. account cash).' => 'Es ist nur sinnvoll Änderungen vorzunehmen, wenn das Konto KEIN Sammelkonto ist und wenn ein gültiger Steuerschlüssel für das Konto existiert. Gleichzeitig Haken bei Forderungen und Verbindlichkeiten zu setzen, macht auch NUR für den Zahlungsein- und Ausgang (bspw. Bank oder Kasse) Sinn.',
'Decrease' => 'Verringern',
'Default (no language selected)' => 'Standard (keine Sprache ausgewählt)',
'Default Accounts' => 'Standardkonten',
- 'Default Bin' => '',
+ 'Default Bin' => 'Standard-Lagerplatz',
'Default Customer/Vendor Language' => 'Standard-Kunden-/Lieferantensprache',
- 'Default Warehouse' => '',
+ 'Default Warehouse' => 'Standard-Lager',
'Default buchungsgruppe' => 'Standardbuchungsgruppe',
'Default output medium' => 'Standardausgabekanal',
'Default printer' => 'Standarddrucker',
'If you want to change any of these parameters then press the "Back" button, edit the file "config/kivitendo.conf" and login into the admin module again.' => 'Wenn Sie einen der Parameter ändern wollen, so drücken Sie auf den "Zurück"-Button, bearbeiten Sie die Datei "config/kivitendo.conf", und melden Sie sich erneut im Administrationsbereich an.',
'If you want to delete such a dataset you have to edit the user(s) that are using the dataset in question and have them use another dataset.' => 'Wenn Sie eine solche Datenbank löschen wollen, so müssen Sie zuerst die Benutzer bearbeiten, die die fragliche Datenbank benutzen, und sie so ändern, dass sie eine andere Datenbank benutzen.',
'If you want to set up the authentication database yourself then log in to the administration panel. kivitendo will then create the database and tables for you.' => 'Wenn Sie die Authentifizierungs-Datenbank selber einrichten wollen, so melden Sie sich im Administrationsbereich an. kivitendo wird dann die Datenbank und die erforderlichen Tabellen für Sie anlegen.',
+ 'If your old bins match exactly Bins in the Warehouse CLICK on <b>AUTOMATICALLY MATCH BINS</b>.' => 'Falls die alte Lagerplatz-Beschreibung in Stammdaten genau mit einem Lagerplatz in einem vorhandenem Lager übereinstimmt, KLICK auf <b>LAGERPLÄTZE AUTOMATISCH ZUORDNEN</b>',
'Illegal characters have been removed from the following fields: #1' => 'Ungültige Zeichen wurden aus den folgenden Feldern entfernt: #1',
'Image' => 'Grafik',
'Import' => 'Import',
'Marked as paid' => 'Als bezahlt markiert',
'Marked entries printed!' => 'Markierte Einträge wurden gedruckt!',
'Master Data' => 'Stammdaten',
+ 'Master Data Bin Text Deleted' => 'Gelöschte Stammdaten Freitext-Lagerplätze',
'Max. Dunning Level' => 'höchste Mahnstufe',
'May' => 'Mai',
'May ' => 'Mai',
'Others' => 'Andere',
'Otherwise all users will only have access to their own settings.' => 'Andernfalls haben alle Benutzer nur Zugriff auf ihre Einstellungen.',
'Otherwise the variable is only available for printing.' => 'Andernfalls steht die Variable nur beim Ausdruck zur Verfügung.',
+ 'Otherwise you can simply check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' => 'Andernfalls einfach Lager und Lagerplätze erschaffen anhaken und einen Namen für das Lager vergeben (Lagerplätze werden dann automatisch hinzugefügt) danach auf weiter',
'Out of balance transaction!' => 'Buchung ist nicht ausgeglichen!',
'Out of balance!' => 'Summen stimmen nicht berein!',
'Output Number Format' => 'Zahlenformat (Ausgabe)',
'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
'The AP transaction #1 has been deleted.' => 'Die Kreditorenbuchung #1 wurde gelöscht.',
'The AR transaction #1 has been deleted.' => 'Die Debitorenbuchung #1 wurde gelöscht.',
+ 'The Bins in Inventory were only a information text field.' => 'Die Lagerplätze unter Stammdaten/Waren, sind nur ein informatives Textfeld.',
+ 'The Bins in master data were only a information text field.' => 'Die Lagerplätze unter Stammdaten/Waren, sind nur ein informatives Textfeld.',
'The GL transaction #1 has been deleted.' => 'Die Dialogbuchung #1 wurde gelöscht.',
'The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.' => 'Der LDAP-Server "#1:#2" ist nicht erreichbar. Bitte überprüfen Sie die Angaben in config/kivitendo.conf.',
'The SEPA export has been created.' => 'Der SEPA-Export wurde erstellt',
'There are #1 more open invoices for this customer with other currencies.' => 'Es gibt #1 weitere offene Rechnungen für diesen Kunden, die in anderen Währungen ausgestellt wurden.',
'There are #1 more open invoices from this vendor with other currencies.' => 'Es gibt #1 weitere offene Rechnungen von diesem Lieferanten, die in anderen Währungen ausgestellt wurden.',
'There are #1 unfinished follow-ups of which #2 are due.' => 'Es gibt #1 Wiedervorlage(n), von denen #2 fällig ist/sind.',
+ 'There are Bins defined in your Inventory.' => 'Unter Stammdaten/Waren sind Lagerplätze defininert',
+ 'There are Bins defined in your master data.' => 'Unter Stammdaten/Waren sind Lagerplätze defininert',
'There are bookings to the account 3803 after 01.01.2007. If you didn\'t change this account manually to 19% the bookings are probably incorrect.' => 'Das Konto 3803 wurde nach dem 01.01.2007 bebucht. Falls Sie dieses Konto nicht manuell auf 19% gestellt haben sind die Buchungen wahrscheinlich mit falscher Umsatzsteuer gebucht worden.',
'There are double partnumbers in your database.' => 'In ihrer Datenbank befinden sich mehrfach vergebene Artikelnummern.',
'There are entries in tax where taxkey is NULL.' => 'In der Datenbank sind Steuern ohne Steuerschlüssel vorhanden (in der Tabelle tax Spalte taxkey).',
'This customer number is already in use.' => 'Diese Kundennummer wird bereits verwendet.',
'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => 'Dieses Feature vermeidet insbesondere Verwechslungen von Umsatz- und Vorsteuer.',
'This group will be called "Full Access".' => 'Diese Gruppe wird "Vollzugriff" genannt.',
+ 'This has been changed in this version, therefore please change the "old" bins to some real warehouse bins.' => 'Das wurde in dieser Version umgestellt, bitte ändern Sie die Freitext-Lagerplätze auf wirkliche Lagerplätze.',
+ 'This has been changed in this version.' => 'Ab dieser Version ist dies nicht mehr so.',
'This installation uses an unknown chart of accounts ("#1"). This database upgrade cannot create standard buchungsgruppen automatically.' => 'Diese Installation benutzt einen unbekannten Kontenrahmen ("#1"). Dieses Datenbankupgrade kann die Standardbuchungsgruppen nicht automatisch anlegen.',
'This is a preliminary check for existing sources. Nothing will be created or deleted at this stage!' => 'In diesem Schritt werden bestehende Datenbanken gesucht. Es werden noch keine Änderungen vorgenommen!',
'This is a very critical problem.' => 'Dieses Problem ist sehr schwerwiegend.',
+ 'This is the default bin for parts' => 'Standard-Lagerplatz für Stammdaten/Waren',
'This list is capped at 15 items to keep it fast. If you need a full list, please use reports.' => 'Diese Liste ist auf 15 Zeilen begrenzt. Wenn Sie eine vollständige Liste benötigen, erstellen Sie bitte einen Bericht.',
'This means that the user has created an AP transaction and chosen a taxkey for sales taxes, or that he has created an AR transaction and chosen a taxkey for input taxes.' => 'Das bedeutet, dass ein Benutzer eine Kreditorenbuchung angelegt und in ihr einen Umsatzsteuer-Steuerschlüssel verwendet oder eine Debitorenbuchung mit Vorsteuer-Steuerschlüssel angelegt hat.',
'This module can help you identify and correct such entries by analyzing the general ledger and presenting you likely solutions but also allowing you to fix problems yourself.' => 'Dieses Modul kann Ihnen helfen, problematische Einträge im Hauptbuch zu identifizieren und teilweise zu beheben. Dabei werden je nach Problem mögliche Lösungen aufgezeigt, wobei Sie die entscheiden können, welche Probleme automatisch gelöst werden sollen.',
'This upgrade script tries to map all existing parts in the database to the newly created Buchungsgruppen.' => 'Dieses Upgradescript versucht, bei allen bestehenden Artikeln neu erstellte Buchungsgruppen zuzuordnen.',
'This upgrade script tries to map all existing units in the database to the newly created units.' => 'Dieses Update-Script versucht, alle bestehenden Einheiten automatisch in die neuen Einheiten umzuwandeln.',
'This vendor number is already in use.' => 'Diese Lieferantennummer wird bereits verwendet.',
+ 'Three Options:' => 'Drei Optionen:',
'Time period for the analysis:' => 'Analysezeitraum:',
'Timestamp' => 'Uhrzeit',
'Title' => 'Titel',
'Unbalanced Ledger' => 'Bilanzfehler',
'Unchecked custom variables will not appear in orders and invoices.' => 'Unmarkierte Variablen werden für diesen Artikel nicht in Aufträgen und Rechnungen angezeigt.',
'Unfinished follow-ups' => 'Nicht erledigte Wiedervorlagen',
+ 'Unfortunately you have no warehouse defined.' => 'Leider, gibt es kein Lager in diesem Mandanten.',
'Unit' => 'Einheit',
'Unit (if missing or empty default unit will be used)' => 'Einheit (falls nicht vorhanden oder leer wird die Standardeinheit benutzt)',
'Unit missing.' => 'Die Einheit fehlt.',
--- /dev/null
+-- @tag: add_warehouse_defaults
+-- @description: Standardlager und Lagerplatz in der Tabelle defaults. Sowie als ID-Verknüpfung in parts
+-- @depends: release_3_0_0
+-- @charset: utf-8
+ALTER TABLE defaults ADD COLUMN warehouse_id integer references warehouse(id);
+ALTER TABLE defaults add column bin_id integer references bin(id);
+ALTER TABLE parts ADD COLUMN warehouse_id integer references warehouse(id);
+ALTER TABLE parts add column bin_id integer references bin(id);
+
--- /dev/null
+# @tag: default_bin_parts
+# @description: Freitext Feld Lagerplatz nach Lager und Lagerplatz migrieren
+# @depends: release_3_0_0 add_warehouse_defaults
+
+package SL::DBUpgrade2::default_bin_parts;
+
+use strict;
+use utf8;
+use Data::Dumper;
+use SL::DBUtils;
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+ my ($self) = @_;
+ $::form->get_lists('warehouses' => { 'key' => 'WAREHOUSES',
+ 'bins' => 'BINS', });
+ if (scalar @{ $::form->{WAREHOUSES} }) {
+ $::form->{warehouse_id} ||= $::form->{WAREHOUSES}->[0]->{id};
+ $::form->{bin_id} ||= $::form->{WAREHOUSES}->[0]->{BINS}->[0]->{id};
+ } else {
+ $::form->{NO_WAREHOUSE} = 1;
+ }
+ $::form->{warehouse_id} = 0; # 0 ist die ID für leere Option
+
+ if ( $::form->{'continued'} ) {
+ my $CREATE_BINS = 0;
+ my $CREATE_WAREHOUSE = 0;
+ if (!defined($::form->{NO_WAREHOUSE}) && defined($::form->{create_new_bins}) && $::form->{warehouse_id_default}) {
+ $CREATE_BINS = 1;
+ }
+ if (defined($::form->{NO_WAREHOUSE}) && defined($::form->{create_new_bins}) && $::form->{new_warehouse}) {
+ $CREATE_WAREHOUSE = 1;
+ $CREATE_BINS = 1;
+ }
+
+ # Lager anlegen
+ my $insert_warehouse_query = qq|INSERT into warehouse (description, invalid, sortkey) VALUES (?, 'false', 1) |;
+ my $prepared_insert_warehouse_query = $self->dbh->prepare($insert_warehouse_query) || $self->db_error($insert_warehouse_query);
+
+ # Lagerplatz anlegen
+ my $insert_bin_query = qq|INSERT into bin (description, warehouse_id) VALUES (?, ?) |;
+ my $prepared_insert_bin_query = $self->dbh->prepare($insert_bin_query) || $self->db_error($insert_bin_query);
+
+ # Lagerplatz aus Liste zuweisen
+ my $update_query = qq|UPDATE parts SET warehouse_id = ?, bin_id = ? WHERE id = ?|;
+ my $prepared_update_query = $self->dbh->prepare($update_query) || $self->db_error($update_query);
+
+
+ # gerade angelegten Lagerplatz zuweisen
+ my $update_new_bin_query = qq|UPDATE parts SET warehouse_id = (SELECT warehouse_id from bin where description = ?),
+ bin_id = (SELECT id from bin where description = ?)
+ WHERE id = ?|;
+ my $prepared_update_new_bin_query = $self->dbh->prepare($update_new_bin_query) || $self->db_error($update_new_bin_query);
+
+
+ # kein lager vorhanden, aber wir legen ein neues an.
+ if ($CREATE_WAREHOUSE && $CREATE_BINS) {
+ $prepared_insert_warehouse_query->execute($::form->{new_warehouse}) || $self->db_error($insert_warehouse_query);
+ $prepared_insert_warehouse_query->finish();
+ my $query = qq|SELECT id FROM warehouse LIMIT 1;|;
+ my $sth = $self->dbh->prepare($query);
+ $sth->execute || $::form->dberror($query);
+ $::form->{warehouse_id_default} = $sth->fetchrow_array();
+ }
+
+ foreach my $i (1 .. $::form->{rowcount}) {
+
+ # Best Case: Lagerplatz aus Liste gewählt
+ if ($::form->{"bin_id_$i"}) {
+ $prepared_update_query->execute($::form->{"warehouse_id_$i"}, $::form->{"bin_id_$i"}, $::form->{"partid_$i"}) || $self->db_error($update_query);
+ } elsif ($CREATE_BINS) {
+ # Lager vorhanden, bzw. vorher erstellt. alte bins automatisch hinzufügen und zum Standardlagerplatz verdrahten
+ $prepared_insert_bin_query->execute($::form->{"bin_$i"}, $::form->{warehouse_id_default}) || $self->db_error($insert_bin_query);
+ $prepared_update_new_bin_query->execute($::form->{"bin_$i"}, $::form->{"bin_$i"}, $::form->{"partid_$i"}) || $self->db_error($update_new_bin_query);
+ }
+ }
+ $prepared_insert_bin_query->finish();
+ $prepared_update_new_bin_query->finish();
+ $prepared_update_query->finish();
+ $::form->{FINISH} = 1;
+ # das alte textfeld entfernen
+ #my $query = qq|ALTER TABLE parts drop COLUMN bin|;
+ #$self->db_query($query);
+ #return 1;
+ }
+
+ my $query = qq|SELECT id, partnumber, description, bin
+ FROM parts pa
+ WHERE '' <> NULLIF ( bin, '')
+ ORDER BY partnumber;|;
+
+ my $sth = $self->dbh->prepare($query);
+ $sth->execute || $::form->dberror($query);
+
+ $::form->{PARTS} = [ selectall_hashref_query($::form, $self->dbh, $query) ];
+
+ if ( (scalar @{ $::form->{PARTS} } > 0 ) && !$::form->{NO_WAREHOUSE} && !$::form->{FINISH} ) {
+ &print_error_message;
+ return 2;
+ } elsif ( (scalar @{ $::form->{PARTS} } > 0 ) && $::form->{NO_WAREHOUSE} && !$::form->{FINISH} ) {
+ &print_error_message_no_warehouse;
+ return 2;
+ }
+ # das alte textfeld entfernen
+ # hier nochmal, da oben schon ein return 1 gesetzt ist
+ my $query = qq|ALTER TABLE parts drop COLUMN bin|;
+ $self->db_query($query);
+ return 1;
+}
+
+sub print_error_message {
+ print $::form->parse_html_template("dbupgrade/default_bin_parts");
+}
+
+sub print_error_message_no_warehouse {
+ print $::form->parse_html_template("dbupgrade/default_bin_parts_no_warehouse");
+}
+
+
+1;
--- /dev/null
+[%- USE T8 %]
+[%- USE HTML %]
+[%- USE LxERP %]
+[%- USE JavaScript %]
+ <script type="text/javascript" src="js/common.js"></script>
+ <script type="text/javascript" src="js/parts_language_selection.js"></script>
+ <script type="text/javascript">
+ <!--
+ warehouses = new Array();
+ [%- USE WAREHOUSES_it = Iterator(WAREHOUSES) %][%- FOREACH warehouse = WAREHOUSES_it %]
+ warehouses[[% WAREHOUSES_it.count - 1 %]] = new Array();
+ warehouses[[% WAREHOUSES_it.count - 1 %]]['id'] = [% warehouse.id %];
+ warehouses[[% WAREHOUSES_it.count - 1 %]]['bins'] = new Array();
+ [% USE BINS_it = Iterator(warehouse.BINS) %][% FOREACH bin = BINS_it %]
+ warehouses[[% WAREHOUSES_it.count - 1%]]['bins'][[% BINS_it.count - 1 %]] = new Array();
+ warehouses[[% WAREHOUSES_it.count - 1%]]['bins'][[% BINS_it.count - 1 %]]['description'] = "[% JavaScript.escape(bin.description) %]";
+ warehouses[[% WAREHOUSES_it.count - 1%]]['bins'][[% BINS_it.count - 1 %]]['id'] = [% bin.id %];
+ [% END %]
+ [% END %]
+ //var rowcount = [% rowcount %];
+ //var rowcount = 3; //[% rowcount %];
+ function warehouse_selected(warehouse_id, bin_id, loop) {
+ var control = document.getElementById("bin_id_" + loop);
+
+ for (var i = control.options.length - 1; i >= 0; i--) {
+ control.options[i] = null;
+ }
+
+ var warehouse_index = 0;
+
+ for (i = 0; i < warehouses.length; i++)
+ if (warehouses[i]['id'] == warehouse_id) {
+ warehouse_index = i;
+ break;
+ }
+
+ var warehouse = warehouses[warehouse_index];
+ var bin_index = 0;
+
+ for (i = 0; i < warehouse['bins'].length; i++)
+ if (warehouse['bins'][i]['id'] == bin_id) {
+ bin_index = i;
+ break;
+ }
+
+ for (i = 0; i < warehouse['bins'].length; i++) {
+ control.options[i] = new Option(warehouse['bins'][i]['description'], warehouse['bins'][i]['id']);
+ }
+
+
+ control.options[bin_index].selected = true;
+ }
+ function bin_match(rowcount) {
+ for (i = 1; i < rowcount + 1; i++) { // über alle parts_id
+ var lagerplatz = document.getElementById("bin_" + i).value;
+ var control = document.getElementById("bin_id_" + i);
+ var bin_index = 0;
+ //alert(lagerplatz);
+ for (j = 0; j < warehouses.length; j++) { // über alle lager
+ var warehouse = warehouses[j];
+
+ for (k = 0; k < warehouse['bins'].length; k++) { // über alle lagerplätze
+
+ if (lagerplatz == warehouse['bins'][k]['description']) {
+ //alert('ware ' + warehouse['bins'][k]['description']);
+ var lager = document.getElementById("warehouse_id_" + i);
+ lager.selectedIndex = j;
+ bin_index = k;
+ break;
+ /*var lagerplatz = document.getElementById("bin_id_" + i);
+ alert('lagerplatz ' + lagerplatz.value);
+ lagerplatz.selectedIndex = k; */
+ }
+
+ }
+ }
+ }
+ for (i = 0; i < warehouse['bins'].length; i++) {
+ control.options[i] = new Option(warehouse['bins'][i]['description'], warehouse['bins'][i]['id']);
+ }
+ control.options[bin_index].selected = true;
+
+
+ }
+
+
+ $(function() {
+ warehouse_selected([% warehouse_id %], [% bin_id %]);
+ })
+ -->
+ </script>
+
+
+
+<div class="listtop">[% '<b>Default Bins Migration !READ CAREFULLY!</b>' | $T8 %]</div>
+<form name="Form" method="post" action="login.pl">
+<input type="hidden" name="action" value="login">
+<input type="hidden" name="continued" value="1">
+
+
+
+<p>[% 'There are Bins defined in your Inventory.' | $T8 %]</p>
+<p>[% 'The Bins in Inventory were only a information text field.' | $T8 %]</p>
+<p>[% 'This has been changed in this version, therefore please change the "old" bins to some real warehouse bins.' | $T8 %]</p>
+<p>[% 'If your old bins match exactly Bins in the Warehouse CLICK on <b>AUTOMATICALLY MATCH BINS</b>.' | $T8 %]</p>
+<p>[% 'Otherwise you can simply check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' | $T8 %]</p>
+
+<p>[% 'Please change the partnumber of the following parts and run the update again:' | $T8 %]</p>
+<table>
+ <tr>
+ <th class="listheading">[% 'Partnumber' | $T8 %]</th>
+ <th class="listheading">[% 'Description' | $T8 %]</th>
+ <th class="listheading">[% 'Bin' | $T8 %]</th>
+ <th class="listheading">[% 'Default Warehouse' | $T8 %]</th>
+ <th class="listheading">[% 'Default Bin' | $T8 %]</th>
+ </tr>
+
+ [% SET row_odd = '1' %][% FOREACH row = PARTS %]
+ <tr class="listrow[% IF row_odd %]1[% SET row_odd = '0' %][% ELSE %]0[% SET row_odd = '1' %][% END %]">
+ <td align="left"> [% HTML.escape(row.partnumber) %]</a></td>
+ <td align="left"> [% HTML.escape(row.description) %]</a></td>
+ <td align="right">[% HTML.escape(row.bin) %]
+ <input type="hidden" id="bin_[% loop.count %]" name="bin_[% loop.count %]" value="[% HTML.escape(row.bin) %]">
+ </td>
+ <td>
+ <input type="hidden" name='partid_[% loop.count %]' value='[% HTML.escape(row.id) %]'>
+ <select id="warehouse_id_[% loop.count %]" name="warehouse_id_[% loop.count %]" onchange="warehouse_selected(warehouses[this.selectedIndex]['id'], 0, [% loop.count %])">
+ [%- FOREACH warehouse = WAREHOUSES %]
+ <option value="[% HTML.escape(warehouse.id) %]"[% IF warehouse_id == warehouse.id %] selected[% END %]>[% warehouse.description %]</option>
+ [%- END %]
+ <option value="" [% IF warehouse_id == 0 %] selected[% END %] ></option>
+ </select>
+ </td>
+ <td><select id="bin_id_[% loop.count %]" name="bin_id_[% loop.count %]"></select></td>
+ </tr>
+ [% SET rowcount = loop.count %]
+ [% END %]
+ <input type="hidden" name="rowcount" value="[% rowcount %]">
+ <tr><td colspan="5"><hr/></td></tr>
+ <tr><td colspan="5">
+ <input type="checkbox" name="create_new_bins"> [% '<b>Automatically create new bins</b> in the following warehouse if not selected in the list above' | $T8 %]
+ <select id="warehouse_id_default" name="warehouse_id_default">
+ [%- FOREACH warehouse = WAREHOUSES %]
+ <option value="[% HTML.escape(warehouse.id) %]"[% IF warehouse_id == warehouse.id %] selected[% END %]>[% warehouse.description %]</option>
+ [%- END %]
+ <option value="" [% IF warehouse_id == 0 %] selected[% END %] ></option>
+ </select>
+ </td>
+ </tr>
+
+ <tr><td colspan="5"><hr/></td></tr>
+ <tr><td><input type="button" class="submit" onclick="history.back()" value="[% 'Back' | $T8 %]"> </td><td colspan="3" align="center"><b><input style="background-color:#FFEE66" type="button" value="[% 'AUTOMATICALLY MATCH BINS' | $T8 %]" onclick="bin_match([% rowcount %])"></b> </td><td><input type="submit" value="[% 'Continue' | $T8 %]"></td></tr>
+</table>
+</form>
--- /dev/null
+[%- USE T8 %]
+[%- USE HTML %]
+[%- USE LxERP %]
+
+<div class="listtop">[% '<b>Default Bins Migration !READ CAREFULLY!</b>' | $T8 %]</div>
+<form name="Form" method="post" action="login.pl">
+<input type="hidden" name="action" value="login">
+<input type="hidden" name="continued" value="1">
+
+<p>[% 'There are Bins defined in your master data.' | $T8 %]</p>
+<p>[% 'The Bins in master data were only a information text field.' | $T8 %]</p>
+<p>[% 'This has been changed in this version.' | $T8 %]</p>
+<p>[% 'Unfortunately you have no warehouse defined.' | $T8 %]</p>
+
+<p>[% 'Three Options:' | $T8 %]</p>
+<p>[% '<b> I DO NOT CARE</b> Please click continue and the following data (see list) will be deleted:' | $T8 %]</p>
+<p>[% '<b> I DO CARE!</b> Please click back and cancel the update and come back after there has been at least one warehouse defined with bin(s).:' | $T8 %]</p>
+<p>[% '<b> I DO CARE!</b> Please check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' | $T8 %]</p>
+
+<table>
+ <tr>
+ <th class="listheading">[% 'Partnumber' | $T8 %]</th>
+ <th class="listheading">[% 'Description' | $T8 %]</th>
+ <th class="listheading">[% 'Master Data Bin Text Deleted' | $T8 %]</th>
+ </tr>
+
+ [% SET row_odd = '1' %][% FOREACH row = PARTS %]
+ <tr class="listrow[% IF row_odd %]1[% SET row_odd = '0' %][% ELSE %]0[% SET row_odd = '1' %][% END %]">
+ <td align="left"> [% HTML.escape(row.partnumber) %]</a></td>
+ <td align="left"> [% HTML.escape(row.description) %]</a></td>
+ <td align="right">[% HTML.escape(row.bin) %]
+ <input type="hidden" name='partid_[% loop.count %]' value='[% HTML.escape(row.id) %]'>
+ <input type="hidden" id="bin_[% loop.count %]" name="bin_[% loop.count %]" value="[% HTML.escape(row.bin) %]">
+ </tr>
+ [% SET rowcount = loop.count %]
+ [% END %]
+ <input type="hidden" name="rowcount" value="[% rowcount %]">
+<tr><td colspan="5"><hr/></td></tr>
+<tr><td colspan="5"><input type="checkbox" name="create_new_bins"> [% '<b>Automatically create new bins</b> in the following new warehouse ' | $T8 %] <input type="text" name="new_warehouse"></td></tr>
+<tr><td colspan="5"><hr/></td></tr>
+<tr><td><input type="button" class="submit" onclick="history.back()" value="[% 'Back' | $T8 %]"> </td><td colspan="3" align="center"><td><input style="background-color:#FA1400" type="submit" value="[% 'Continue' | $T8 %]"></td></tr>
+</table>
+</form>
-[%- USE T8 %]
+[[%- USE T8 %]
[%- USE HTML %]
[%- USE LxERP %]
- <script type="text/javascript" src="js/common.js"></script>
- <script type="text/javascript" src="js/parts_language_selection.js"></script>
-
+[% PROCESS 'common/select_warehouse_bin.html' %]
<p><div class="listtop">[% title %] [% HTML.escape(partnumber) %] [% HTML.escape(description) %]</div></p>
[% PROCESS 'common/flash.html' %]
<td><input name="rop" size="10" value="[% LxERP.format_amount(rop) %]"></td>
</tr>
<tr>
- <th align="right" nowrap="true">[% 'Bin' | $T8 %]</th>
- <td><input name="bin" size="10" value="[% HTML.escape(bin) %]"></td>
+ <th align="right" nowrap="true">[% 'Default Warehouse' | $T8 %] [% HTML.escape(bin_id) %]</th>
+ <td>
+ <select name="warehouse_id" onchange="warehouse_selected(warehouses[this.selectedIndex]['id'], 0)">
+ [%- FOREACH warehouse = WAREHOUSES %]
+ <option value="[% HTML.escape(warehouse.id) %]"[% IF warehouse_id == warehouse.id %] selected[% END %]>[% warehouse.description %]</option>
+ [%- END %]
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <th align="right" nowrap="true">[% 'Default Bin' | $T8 %]</th>
+ <td><select id="bin_id" name="bin_id"></select></td>
</tr>
[%- END %]
<tr>