ea2fdd0c3c4d22234da3f4803b93ad8410d37903
2 our $partner = "cyclofficine_ivry";
4 our $VERSION = '2013.10.27';
6 use warnings FATAL
=> qw(all);
8 use open qw
/:std :utf8/;
13 #require Text::CSV::Encoded;
14 require XML
::Generator
;
19 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*$});
20 return "20$yy-$mm-$jj";
22 sub parse_amount
(@
) {
31 $_ = Text
::Trim
::trim
($_);
32 if (exists $amounts{$_}) {
36 ($_) = ($_ =~ m{^\s*([0-9]+),00\s*€?.$});
40 sub parse_payment_mean
(@
) {
47 $_ = Text
::Trim
::trim
($_);
48 return exists $payment_means{$_}
52 sub parse_discount
($) {
55 ( "Chômeur" => "unemployed"
56 , "Chmeur" => "unemployed"
57 , "Atelier vélo IdF" => "velorution_idf"
58 , "Étudiant" => "student"
59 , "Etudiant" => "student"
60 , "etudiant" => "student"
61 , "Retraité" => "retired"
63 $_ = Text
::Trim
::trim
($_);
64 return exists $discounts{$_}
68 sub parse_gender
(@
) {
71 ( "Ass." => "association"
76 $_ = Text
::Trim
::trim
($_);
77 return exists $genders{$_}
82 our $last_number = -1;
83 our $greatest_number = -1;
84 sub member_of_csv_line
(@
) {
85 my ($members, $csv_line, $csv_lines_rejected, $csv_lines_to_reparse) = @_;
86 print STDERR
("member_of_csv_line: csv_line=".Data
::Dumper
::Dumper
($csv_line));
87 my $number = Text
::Trim
::trim
($csv_line->{number
});
89 push @
$csv_lines_rejected, $csv_line;
93 if (not $number or not ($number =~ m/^[0-9]+$/)) {
94 if (defined $csv_lines_to_reparse) {
95 push @
$csv_lines_to_reparse, $csv_line;
99 $greatest_number = $greatest_number + 1;
100 print STDERR
"WARNING: renumérotation: ".($number?
$number:"undef")." -> $greatest_number\n";
102 =($csv_line->{comment
}?
"$csv_line->{comment}. ":"")
103 ."(n° malformé d'origine : ".($number?
$number:"undef").")";
104 $number = "$greatest_number";
107 if ($number =~ m/^[0-9]+$/) {
108 $number = $number + 0;
109 if ($last_number + 1 != $number + 0) {
110 print STDERR
"WARNING: discontinuité: attendu=".($last_number + 1)." eu=".($number + 0)."\n";
112 $last_number = $number;
113 $greatest_number = $number
114 if $number > $greatest_number;
116 if ($csv_line->{name
} or $csv_line->{email
}) {
117 if (exists $members->{$number}) {
118 $member = $members->{$number};
122 $members->{$number} = $member;
124 $member->{number
} = $number;
127 unless $member->{name
};
129 = $csv_line->{firstname
}
130 unless $member->{firstname
};
133 unless $member->{email
};
134 $member->{cotisations
}
136 unless exists $member->{cotisations
};
137 push @
{$member->{cotisations
}},
138 { amount
=> parse_amount
($csv_line->{cotisation_amount
})
139 , date
=> parse_date
($csv_line->{cotisation_date
})
140 , discount
=> parse_discount
($csv_line->{cotisation_discount
})
141 , mean
=> parse_payment_mean
($csv_line->{cotisation_mean
})
147 unless $member->{zip
};
149 =($csv_line->{gender
}
150 ? parse_gender
($csv_line->{gender
})
152 unless $member->{gender
};
156 : ($member->{zip
} and ($member->{zip
} =~ m/^750[0-2][0-9]$/) ?
"Paris" : undef))
157 unless $member->{city
};
159 = ($member->{zip
} and ($member->{zip
} =~ m/^UK$/)
162 unless $member->{country
};
164 =($csv_line->{landline_phone
}
165 ?
$csv_line->{landline_phone
}
166 :($csv_line->{mobile_phone
}
167 ?
$csv_line->{mobile_phone
}
169 unless $member->{phone
};
171 =($csv_line->{street
}
172 ?
$csv_line->{street
}
174 unless $member->{street
};
176 =($csv_line->{comment
}
177 ?
$csv_line->{comment
}
179 unless $member->{comment
};
182 push @
$csv_lines_rejected, $csv_line;
187 sub xml_of_member
(@
) {
188 my ($xml, $member, $xml_data, $xml_data_noupdate) = @_;
189 print STDERR
("xml_of_member: member=".Data
::Dumper
::Dumper
($member));
190 push @
$xml_data, $xml->record
191 ( { id
=> "res_partner_${partner}_".$member->{number
}
192 , model
=> "res.partner"
194 , $xml->field({name
=> "name"}, $member->{name
} . ($member->{firstname
} ?
" ".$member->{firstname
} : ""))
195 , $xml->field({name
=> "member_ident"}, $member->{number
})
196 , $xml->field({name
=> "type"}, "default")
197 , ($member->{zip
} ?
$xml->field({name
=> "zip"}, $member->{zip
}) : ())
198 , ($member->{city
} ?
$xml->field({name
=> "city"}, $member->{city
}) : ())
199 , ($member->{country
}?
$xml->field({name
=> "country_id", model
=> "res.country", search
=> "[('name','=','".$member->{country
}."')]"}) : ())
200 , ($member->{email
} ?
$xml->field({name
=> "email"}, $member->{email
}) : ())
201 , ($member->{phone
} ?
$xml->field({name
=> "phone"}, $member->{phone
}) : ())
202 , ($member->{street
} ?
$xml->field({name
=> "street"}, $member->{street
}) : ())
203 , ($member->{comment
}?
$xml->field({name
=> "comment"}, $member->{comment
}) : ())
205 die unless $member->{cotisations
};
206 foreach my $cotisation (@
{$member->{cotisations
}}) {
207 push @
$xml_data_noupdate, $xml->function
208 ( { model
=> "account.invoice"
209 , name
=> "pay_and_reconcile"
211 , $xml->xmlcmnt('ids')
213 ( { model
=> "account.invoice"
214 , name
=> "draft2open"
217 ( { model
=> "res.partner"
218 , name
=> "create_membership_invoice"
220 , $xml->xmlcmnt('partner_id')
221 , $xml->value({eval => "ref('res_partner_${partner}_".$member->{number
}."')"})
222 , $xml->xmlcmnt('product_id')
223 , $xml->value({eval => "ref('product_".$cotisation->{discount
}."_member')"})
224 , $xml->xmlcmnt('context')
225 , $xml->value({eval => "{'amount':".$cotisation->{amount
}.", 'date_from':'".$cotisation->{date
}."'}"})
228 , $xml->xmlcmnt('pay_amount')
229 , $xml->value ({eval => "$cotisation->{amount}"})
230 , $xml->xmlcmnt('pay_account_id')
231 , $xml->value ({model
=> "account.account", search
=> "[('name', '=', 'Cash')]"})
232 , $xml->xmlcmnt("mean: $cotisation->{mean}")
233 , $xml->xmlcmnt('period_id')
234 , $xml->value ({model
=> "account.period", search
=> "[('name', '=', time.strftime('%m/%Y'))]"})
235 , $xml->xmlcmnt('pay_journal_id')
236 , $xml->value ({model
=> "account.journal", search
=> "[('name', '=', 'Cash')]"})
237 , $xml->xmlcmnt('writeoff_acc_id')
238 , $xml->value ({model
=> "account.account", search
=> "[('name', '=', 'Cash')]"})
239 , $xml->xmlcmnt('writeoff_period_id')
240 , $xml->value ({model
=> "account.period", search
=> "[('name', '=', time.strftime('%m/%Y'))]"})
241 , $xml->xmlcmnt('writeoff_journal_id')
242 , $xml->value ({model
=> "account.journal", search
=> "[('name', '=', 'Cash')]"})
243 , $xml->xmlcmnt('context')
244 , $xml->value ({eval => "{}"})
245 , $xml->xmlcmnt('name')
246 , $xml->value ({eval => "str('Import de paiement automatique')"})
252 my $csv = Text
::CSV
->new
257 my $xml = XML
::Generator
->new
259 , conformance
=> 'strict'
263 my $in = IO
::Wrap
::wraphandle
(\
*STDIN
);
265 my $csv_head = $csv->getline($in);
266 #print STDERR ("head: ", join("|", @$csv_head), "\n");
267 #$csv->column_names(@$csv_head);
268 $csv->column_names (qw
(
287 my $xml_data_noupdate = [];
288 my $csv_lines_rejected = [];
289 my $csv_lines_to_reparse = [];
291 while (my $csv_line = $csv->getline_hr($in)) {
292 #print STDERR ("csv_line: ", join("|", @$csv_line), "\n");
293 member_of_csv_line
($members, $csv_line, $csv_lines_rejected, $csv_lines_to_reparse);
295 print STDERR
"csv_lines_to_reparse=".Data
::Dumper
::Dumper
($csv_lines_to_reparse);
296 foreach my $csv_line (@
$csv_lines_to_reparse) {
297 member_of_csv_line
($members, $csv_line, $csv_lines_rejected, undef);
299 foreach my $number (sort {$a <=> $b} (keys %$members)) {
300 xml_of_member
($xml, $members->{$number}, $xml_data, $xml_data_noupdate);
302 push @
$xml_data_noupdate, $xml->record
303 ( { id
=> "remembership.member_ident_sequence"
304 , model
=> "ir.sequence"
306 , $xml->field({name
=> "number_next"}, $greatest_number + 1)
308 binmode STDOUT
, ':utf8';
310 ( $xml->data(@
$xml_data)
313 , @
$xml_data_noupdate )
316 my $out = IO
::Wrap
::wraphandle
(\
*STDERR
);
317 print STDERR
"csv_lines_rejected=".Data
::Dumper
::Dumper
($csv_lines_rejected);