--- /dev/null
+#!/usr/bin/perl
+
+our $VERSION = '0.0.1';
+use strict;
+use warnings FATAL => qw(all);
+use utf8;
+use open qw/:std :utf8/;
+require Data::Dumper;
+require Encode;
+require IO::Wrap;
+require Text::CSV;
+#require Text::CSV::Encoded;
+require XML::Generator;
+require Text::Trim;
+
+sub parse_date ($) {
+ my ($_) = @_;
+ if ($_) {
+ my ($jj,$mm,undef,$yy) = ($_ =~ m{^\s*([0-3]?[0-9])\s*/\s*([0-1]?[0-9])\s*/\s*(20)?([0-9][0-9])\s*$});
+ return "20$yy-$mm-$jj";
+ }
+ else { return undef };
+ }
+sub parse_euro ($) {
+ my ($_) = @_;
+ if ($_) {
+ my ($num) = ($_ =~ m{^\s*([0-9]+),00\s*€?.$});
+ return $num;
+ }
+ else { return undef };
+ }
+sub parse_line (@) {
+ my ($pass, $xml, $number_next, $adh, $data, $data_noupdate, $data_rejected, $data_reparse) = @_;
+ my ($number, $date_cot1, $cot1, $moyen_cot1, $reduction, $genre, $prenom, $nom, $naissance
+ , $email, $tel_fixe, $tel_mobile, $addr_postale, $cp, $city, $comment) =
+ #map {Text::Trim::trim($_)}
+ @$adh;
+ my $partner = "cyclofficine_ivry";
+ $number = Text::Trim::trim($number);
+ if (not ($number =~ m/^[0-9]+$/)) {
+ if ($pass == 0) {
+ push @$data_reparse, $adh;
+ return ($number_next);
+ }
+ else {
+ $comment
+ =($comment?"$comment. ":"")
+ ."(n° malformé d'origine : $number)";
+ $number = "$number_next";
+ }
+ }
+ if ($number =~ m/^[0-9]+$/) {
+ if ($nom or $email) {
+ $number = $number + 0;
+ $number_next = $number + 1;
+ $date_cot1 = parse_date($date_cot1);
+ #$date_cot2 = parse_date($date_cot2);
+ $cot1 = parse_euro($cot1);
+ #$cot2 = parse_euro($cot2);
+ my $city = ($cp and ($cp =~ m/^750[0-2][0-9]$/) ? "Paris" : undef);
+ my $country = ($cp and ($cp =~ m/^UK$/) ? "Royaume-Uni" : "France");
+ my $phone = undef;
+ my $street = undef;
+ my %reductions =
+ ( "Chômeur" => "unemployed"
+ , "Chmeur" => "unemployed"
+ , "Atelier vélo IdF" => "velorution_idf"
+ , "Étudiant" => "student"
+ , "Etudiant" => "student"
+ , "etudiant" => "student"
+ , "Retraité" => "retired"
+ );
+ $reduction = Text::Trim::trim($reduction);
+ $reduction
+ = exists $reductions{$reduction}
+ ? $reductions{$reduction}
+ : "standard";
+ my %pay_accounts =
+ ( "Espèces" => "cash"
+ , "Chèque" => "bank"
+ );
+ my $pay_account_cot1 = Text::Trim::trim($moyen_cot1);
+ $pay_account_cot1
+ = exists $pay_accounts{$pay_account_cot1}
+ ? $pay_accounts{$pay_account_cot1}
+ : "cash";
+ push @$data, $xml->record
+ ( { id => "res_partner_${partner}_$number"
+ , model => "res.partner"
+ }
+ , $xml->field({name => "name"}, $nom . ($prenom ? " $prenom" : ""))
+ , $xml->field({name => "member_ident"}, $number)
+ , $xml->field({name => "type"}, "default")
+ , ($cp ? $xml->field({name => "zip"}, $cp) : ())
+ , ($city ? $xml->field({name => "city"}, $city) : ())
+ , ($country ? $xml->field({name => "country_id", model => "res.country", search => "[('name','=','$country')]"}) : ())
+ , ($email ? $xml->field({name => "email"}, $email) : ())
+ , ($phone ? $xml->field({name => "phone"}) : ())
+ , ($addr_postale? $xml->field({name => "street"}, $addr_postale) : ())
+ , ($comment ? $xml->field({name => "comment"}, $comment) : ())
+ );
+ my %cots =
+ ( ($cot1 ? ($cot1 => $date_cot1) : ())
+ #, ($cot2 ? ($cot2 => $date_cot2) : ())
+ ) ;
+ while ( my ($amount, $date_from) = each(%cots) ) {
+ push @$data_noupdate, $xml->function
+ ( { model => "account.invoice"
+ , name => "pay_and_reconcile"
+ }
+ , $xml->xmlcmnt('ids')
+ , $xml->function
+ ( { model => "account.invoice"
+ , name => "draft2open"
+ }
+ , $xml->function
+ ( { model => "res.partner"
+ , name => "create_membership_invoice"
+ }
+ , $xml->xmlcmnt('partner_id')
+ , $xml->value({eval => "ref('res_partner_${partner}_$number')"})
+ , $xml->xmlcmnt('product_id')
+ , $xml->value({eval => "ref('product_${reduction}_member')"})
+ , $xml->xmlcmnt('context')
+ , $xml->value({eval => "{'amount':$amount, 'date_from':'$date_from'}"})
+ )
+ )
+ , $xml->xmlcmnt('pay_amount')
+ , $xml->value ({eval => "$amount"})
+ , $xml->xmlcmnt('pay_account_id')
+ , $xml->value ({model => "account.account", search => "[('name', '=', 'Cash')]"})
+ , $xml->xmlcmnt("moyen_cot1: $moyen_cot1")
+ , $xml->xmlcmnt('period_id')
+ , $xml->value ({model => "account.period", search => "[('name', '=', time.strftime('%m/%Y'))]"})
+ , $xml->xmlcmnt('pay_journal_id')
+ , $xml->value ({model => "account.journal", search => "[('name', '=', 'Cash')]"})
+ , $xml->xmlcmnt('writeoff_acc_id')
+ , $xml->value ({model => "account.account", search => "[('name', '=', 'Cash')]"})
+ , $xml->xmlcmnt('writeoff_period_id')
+ , $xml->value ({model => "account.period", search => "[('name', '=', time.strftime('%m/%Y'))]"})
+ , $xml->xmlcmnt('writeoff_journal_id')
+ , $xml->value ({model => "account.journal", search => "[('name', '=', 'Cash')]"})
+ , $xml->xmlcmnt('context')
+ , $xml->value ({eval => "{}"})
+ , $xml->xmlcmnt('name')
+ , $xml->value ({eval => "str('Import de paiement automatique')"})
+ );
+ }
+ }
+ else {
+ push @$data_rejected, $adh;
+ }
+ }
+ return ($number_next);
+ }
+
+sub main () {
+ my $csv = Text::CSV->new
+ ({ binary => 1, eol => $/
+ , sep_char => ';'
+ });
+ my $xml = XML::Generator->new
+ ( escape => 'always'
+ , conformance => 'strict'
+ , empty => 'self'
+ , pretty => 2
+ );
+ my $in = IO::Wrap::wraphandle(\*STDIN);
+ my $csv_head = $csv->getline($in);
+ #print STDERR ("head: ", join("|", @$csv_head), "\n");
+ $csv->column_names(@$csv_head);
+ my $data = [];
+ my $data_noupdate = [];
+ my $data_rejected = [];
+ my $data_reparse = [];
+ my $number_next = 1;
+ while (my $adh = $csv->getline($in)) {
+ #print STDERR ("line: ", join("|", @$adh), "\n");
+ ($number_next) = parse_line(0, $xml, $number_next, $adh, $data, $data_noupdate, $data_rejected, $data_reparse);
+ }
+ foreach (@$data_reparse) {
+ ($number_next) = parse_line(1, $xml, $number_next, $_, $data, $data_noupdate, $data_rejected, []);
+ }
+ push @$data_noupdate, $xml->record
+ ( { id => "remembership.member_ident_sequence"
+ , model => "ir.sequence"
+ }
+ , $xml->field({name => "number_next"}, $number_next)
+ );
+ binmode STDOUT, ':utf8';
+ print $xml->openerp
+ ( $xml->data(@$data)
+ , $xml->data
+ ( {noupdate=>"1"}
+ , @$data_noupdate )
+ );
+
+ my $out = IO::Wrap::wraphandle(\*STDERR);
+ foreach (@$data_rejected) {
+ $csv->print($out, $_);
+ }
+ }
+
+main;
--- /dev/null
+<openerp>
+ <data>
+ <record id="product_standard_member" model="product.product">
+ <field name="name">Standard member</field>
+ <field name="list_price">25.00</field>
+ <field name="standard_price">0.00</field>
+ <field name="uom_id" ref="product.product_uom_unit"/>
+ <field name="uom_po_id" ref="product.product_uom_unit"/>
+ <field name="type">service</field>
+ <field name="categ_id" ref="product_category_other"/>
+ <field name="supply_method">produce</field>
+ <field name="membership">True</field>
+ <field name="membership_date2date">True</field>
+ </record>
+ <record id="product_student_member" model="product.product">
+ <field name="name">Student member</field>
+ <field name="list_price">15.00</field>
+ <field name="standard_price">0.00</field>
+ <field name="uom_id" ref="product.product_uom_unit"/>
+ <field name="uom_po_id" ref="product.product_uom_unit"/>
+ <field name="type">service</field>
+ <field name="categ_id" ref="product_category_other"/>
+ <field name="supply_method">produce</field>
+ <field name="membership">True</field>
+ <field name="membership_date2date">True</field>
+ </record>
+ <record id="product_unemployed_member" model="product.product">
+ <field name="name">Unemployed member</field>
+ <field name="list_price">15.00</field>
+ <field name="standard_price">0.00</field>
+ <field name="uom_id" ref="product.product_uom_unit"/>
+ <field name="uom_po_id" ref="product.product_uom_unit"/>
+ <field name="type">service</field>
+ <field name="categ_id" ref="product_category_other"/>
+ <field name="supply_method">produce</field>
+ <field name="membership">True</field>
+ <field name="membership_date2date">True</field>
+ </record>
+ <record id="product_retired_member" model="product.product">
+ <field name="name">Retired member</field>
+ <field name="list_price">15.00</field>
+ <field name="standard_price">0.00</field>
+ <field name="uom_id" ref="product.product_uom_unit"/>
+ <field name="uom_po_id" ref="product.product_uom_unit"/>
+ <field name="type">service</field>
+ <field name="categ_id" ref="product_category_other"/>
+ <field name="supply_method">produce</field>
+ <field name="membership">True</field>
+ <field name="membership_date2date">True</field>
+ </record>
+ <record id="product_velorution_idf_member" model="product.product">
+ <field name="name">Vélorution ÎdF member</field>
+ <field name="list_price">15.00</field>
+ <field name="standard_price">0.00</field>
+ <field name="uom_id" ref="product.product_uom_unit"/>
+ <field name="uom_po_id" ref="product.product_uom_unit"/>
+ <field name="type">service</field>
+ <field name="categ_id" ref="product_category_other"/>
+ <field name="supply_method">produce</field>
+ <field name="membership">True</field>
+ <field name="membership_date2date">True</field>
+ </record>
+ </data>
+ </openerp>