diff --git a/addons/14_lamthao_luan_chuyen_noibo.rptdesign b/addons/14_lamthao_luan_chuyen_noibo.rptdesign new file mode 100644 index 0000000000000..0e911d16c4717 --- /dev/null +++ b/addons/14_lamthao_luan_chuyen_noibo.rptdesign @@ -0,0 +1,3499 @@ + + + Eclipse BIRT Designer Version 4.4.0.v201405191524 Build <4.4.0.v20140606-1451> + + + queryText + 2695 + + + queryTimeOut + 2695 + + + rowFetchSize + 2695 + + + cm + auto layout + ltr + 1200 + + + static + true + integer + true + + simple + text-box + + Unformatted + + + + + + + + metadataBidiFormatStr + ILYNN + + + disabledMetadataBidiFormatStr + + + contentBidiFormatStr + ILYNN + + + disabledContentBidiFormatStr + + + org.postgresql.Driver + jdbc:postgresql://localhost:5432/LAMTHAO + postgres + TmluaEtodW9uZzEyMzQ1Ng== + + + + + nulls lowest + + + phieu_xuat + dimension + phieu_xuat + phieu_xuat + + + lenh_xuat + dimension + lenh_xuat + lenh_xuat + + + hoadon + dimension + hoadon + hoadon + + + dia_diem_giao + dimension + dia_diem_giao + dia_diem_giao + + + ma_quanly + dimension + ma_quanly + ma_quanly + + + khoxuat + dimension + khoxuat + khoxuat + + + khonhap + dimension + khonhap + khonhap + + + bang_kiem_soat + dimension + bang_kiem_soat + bang_kiem_soat + + + nguoi_vc + dimension + nguoi_vc + nguoi_vc + + + nhanvien + dimension + nhanvien + nhanvien + + + user_name + dimension + user_name + user_name + + + day + dimension + day + day + + + month + dimension + month + month + + + year + dimension + year + year + + + loai_xuat + dimension + loai_xuat + loai_xuat + + + loai_vanchuyen + dimension + loai_vanchuyen + loai_vanchuyen + + + + + param_1 + active_id + + integer + 4 + 1 + false + true + false + + + + + + 1 + phieu_xuat + string + + + 2 + lenh_xuat + string + + + 3 + hoadon + string + + + 4 + dia_diem_giao + string + + + 5 + ma_quanly + string + + + 6 + khoxuat + string + + + 7 + khonhap + string + + + 8 + bang_kiem_soat + string + + + 9 + nguoi_vc + string + + + 10 + nhanvien + string + + + 11 + user_name + string + + + 12 + day + string + + + 13 + month + string + + + 14 + year + string + + + 15 + loai_xuat + string + + + 16 + loai_vanchuyen + string + + + + LAMTHAO + + + 1 + phieu_xuat + phieu_xuat + string + 12 + + + 2 + lenh_xuat + lenh_xuat + string + 12 + + + 3 + hoadon + hoadon + string + 12 + + + 4 + dia_diem_giao + dia_diem_giao + string + 12 + + + 5 + ma_quanly + ma_quanly + string + 12 + + + 6 + khoxuat + khoxuat + string + 12 + + + 7 + khonhap + khonhap + string + 12 + + + 8 + bang_kiem_soat + bang_kiem_soat + string + 12 + + + 9 + nguoi_vc + nguoi_vc + string + 12 + + + 10 + nhanvien + nhanvien + string + 12 + + + 11 + user_name + user_name + string + 12 + + + 12 + day + day + string + 12 + + + 13 + month + month + string + 12 + + + 14 + year + year + string + 12 + + + 15 + loai_xuat + loai_xuat + string + 12 + + + 16 + loai_vanchuyen + loai_vanchuyen + string + 12 + + + + + + 2.0 + + + + In + + + + 1 + + 4 + 0 + 0 + Unknown + + + + + + + + + + + id + 1 + + 4 + 10 + 0 + NotNullable + + id + + + + id + + 11 + + + + + + + name + 2 + + 12 + 64 + 0 + NotNullable + + name + + + + name + + 64 + + + + + + + +]]> + + + nulls lowest + + + + + + 1 + id + integer + + + + LAMTHAO + + + 1 + id + id + integer + + + + + + nulls lowest + + + day + dimension + day + day + + + month + dimension + month + month + + + year + dimension + year + year + + + + + param_1 + active_id + + integer + 4 + 1 + true + false + + + + + + 1 + day + string + + + 2 + month + string + + + 3 + year + string + + + + LAMTHAO + + + 1 + day + day + string + 12 + + + 2 + month + month + string + 12 + + + 3 + year + year + string + 12 + + + + + + 2.0 + + + + In + + + + 1 + + 4 + 0 + 0 + Unknown + + + + + + + + + + + nam + 1 + + 8 + 17 + 17 + Unknown + + nam + + + + nam + + 25 + + + + + + + thang + 2 + + 8 + 17 + 17 + Unknown + + thang + + + + thang + + 25 + + + + + + + ngay + 3 + + 8 + 17 + 17 + Unknown + + ngay + + + + ngay + + 25 + + + + + + + +]]> + + + + + stt + measure + stt + stt + + + sp + dimension + sp + sp + + + quantity + measure + quantity + quantity + + + price_unit + measure + price_unit + price_unit + + + donvi + dimension + donvi + donvi + + + giatri + measure + giatri + giatri + + + + + param_1 + active_id + + integer + 4 + 1 + true + false + + + + + + 1 + stt + decimal + + + 2 + sp + string + + + 3 + quantity + decimal + + + 4 + price_unit + decimal + + + 5 + donvi + string + + + 6 + giatri + decimal + + + + LAMTHAO + + + 1 + stt + stt + decimal + -5 + + + 2 + sp + sp + string + 12 + + + 3 + quantity + quantity + decimal + 2 + + + 4 + price_unit + price_unit + decimal + 2 + + + 5 + donvi + donvi + string + 12 + + + 6 + giatri + giatri + decimal + 2 + + + + + + 2.0 + + + + In + + + + 1 + + 4 + 0 + 0 + Unknown + + + + + + + + + + + tienhang + 1 + + 2 + 0 + 0 + Unknown + + tienhang + + + + tienhang + + 131089 + + + + + + + name + 2 + + 12 + 64 + 0 + NotNullable + + name + + + + name + + 64 + + + + + + + +]]> + + + + + ng + dimension + ng + ng + + + th + dimension + th + th + + + nam + dimension + nam + nam + + + + + param_1 + active_id + + integer + 4 + 1 + true + false + + + + + + 1 + ng + string + + + 2 + th + string + + + 3 + nam + string + + + + LAMTHAO + + + 1 + ng + ng + string + 12 + + + 2 + th + th + string + 12 + + + 3 + nam + nam + string + 12 + + + + + + 2.0 + + + + In + + + + 1 + + 4 + 0 + 0 + Unknown + + + + + + + + + + + ng + 1 + + 12 + 2147483647 + 0 + Unknown + + ng + + + + ng + + 2147483647 + + + + + + + th + 2 + + 12 + 2147483647 + 0 + Unknown + + th + + + + th + + 2147483647 + + + + + + + nam + 3 + + 12 + 2147483647 + 0 + Unknown + + nam + + + + nam + + 2147483647 + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + a4 + 0.8202083333333334cm + 1.2cm + 0cm + 1cm + none + none + none + none + false + false + 0in + 0in + + + + + 1.1145833333333333in + 7.385416666666667in + date_invoice + + + ng + ng + dataSetRow["ng"] + string + + + th + th + dataSetRow["th"] + string + + + nam + nam + dataSetRow["nam"] + string + + + date_inoive + dataSetRow["ng"]+" "+dataSetRow["th"]+" "+BirtStr.right(dataSetRow["nam"],2)+" " + string + true + + + + 2.75in + + + 2.3541666666666665in + + + 2.28125in + + + 0.875in + + + + + + 0.3645833333333333in + + + + scroll + 0% + 0% + repeat + "Times New Roman" + 12.5pt + normal + italic + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 1pt + 1pt + 1pt + 1pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + date_inoive + + + + + + + "Times New Roman" + 12px + none + none + none + none + 0.625in + 7.416666666666667in + + 7.416666666666667in + + + none + none + none + none + 0.625in + + 10pt + none + none + none + none + right + + + + + "Times New Roman" + 11pt + bold + none + none + none + none + 2.5729166666666665in + 7.385416666666667in + invoiceinfo + + + phieu_xuat + phieu_xuat + val = dataSetRow["phieu_xuat"]; +if(BirtStr.charLength(val)==10) +val= BirtStr.right(val,6) +if(BirtStr.charLength(val)==15) +val=BirtStr.right(val,11) +val + string + true + + + lenh_xuat + lenh_xuat + dataSetRow["lenh_xuat"] + string + + + hoadon + hoadon + dataSetRow["hoadon"] + string + + + dia_diem_giao + dia_diem_giao + dataSetRow["dia_diem_giao"] + string + + + ma_quanly + ma_quanly + dataSetRow["ma_quanly"] + string + + + khoxuat + khoxuat + dataSetRow["khoxuat"] + string + + + khonhap + khonhap + dataSetRow["khonhap"] + string + + + bang_kiem_soat + bang_kiem_soat + dataSetRow["bang_kiem_soat"] + string + + + nguoi_vc + nguoi_vc + var val =dataSetRow["nguoi_vc"]; +if (val =="Bán hàng qua fax") +val = " " +if (val =="Bán hàng qua Fax") +val = " " +val + string + true + + + nhanvien + nhanvien + dataSetRow["nhanvien"] + string + + + day + day + dataSetRow["day"] + string + + + month + month + dataSetRow["month"] + string + + + year + year + BirtStr.right(dataSetRow["year"],2) + string + true + + + loai_xuat + loai_xuat + dataSetRow["loai_xuat"] + string + + + loai_vanchuyen + loai_vanchuyen + dataSetRow["loai_vanchuyen"] +")" + string + true + + + year_1 + year + dataSetRow["year"] + string + + + + 0.6875in + + + 0.4166666666666667in + + + 0.40625in + + + 0.20833333333333334in + + + 1.1041666666666667in + + + 0.6666666666666666in + + + 0.5729166666666666in + + + 0.375in + + + 0.96875in + + + 0.8125in + + + 1.1666666666666667in + + + 0.21875in + + 4 + 1 + + + 1 + 1 + + 12.5pt + normal + 0pt + 0pt + 0pt + 0pt + lenh_xuat + + + + + 12.5pt + normal + 0pt + 0pt + 0pt + 0pt + center + invoiceinfo + + + day + day + dataSetRow["day"] + string + + + day + + + + 1 + 1 + + 12.5pt + normal + 0pt + 0pt + 0pt + 0pt + center + month + + + + + 2 + 1 + + 12.5pt + normal + 0pt + 0pt + 0pt + 0pt + left + year + + + + + + 0.21875in + + 1 + 1 + + + 9 + 1 + + 12.5pt + normal + loai_xuat + + + + + 12.5pt + normal + 7pt + phieu_xuat + + + + + 0.21875in + + 4 + 1 + + + 7 + 1 + + 12.5pt + normal + nguoi_vc + + + + + 0.22916666666666666in + + 4 + 1 + + + 7 + 1 + + 12.5pt + normal + loai_vanchuyen + + + + + 0.22916666666666666in + + 3 + 1 + + + 8 + 1 + + 12.5pt + normal + bang_kiem_soat + + + + + 0.23958333333333334in + + 3 + 1 + + + 7 + 1 + + 12.5pt + normal + dia_diem_giao + + + + scroll + 0% + 0% + repeat + "Times New Roman" + 11pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + left + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + 12.5pt + normal + ma_quanly + + + + + 0.22916666666666666in + + 2 + 1 + + + 9 + 1 + + 12.5pt + normal + nowrap + block + khoxuat + + + + + 0.46875in + + 2 + 1 + + + 9 + 1 + + 12.5pt + normal + inline + khonhap + + + + + + 4.40625in + + + 4.40625in + + + "Times New Roman" + 10pt + bold + none + none + none + none + 7.447916666666667in + thanhtien + + + stt + stt + dataSetRow["stt"] + decimal + + + sp + sp + dataSetRow["sp"] + string + + + quantity + quantity + dataSetRow["quantity"] + decimal + + + price_unit + price_unit + dataSetRow["price_unit"] + decimal + + + donvi + donvi + dataSetRow["donvi"] + string + + + giatri + giatri + dataSetRow["giatri"] + decimal + + + + 0.4375in + + + 2.40625in + + + 0.7291666666666666in + + + 1.0416666666666667in + + + 0.5625in + + + 1.1041666666666667in + + + 1.1666666666666667in + +
+ + none + none + none + none + 0.23958333333333334in + + 1 + 2 + scroll + 0% + 0% + repeat + "Times New Roman" + 12pt + normal + normal + normal + black + none + none + none + none + none + none + none + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + 1 + 2 + none + none + none + none + + + 1 + 2 + none + none + none + none + + + 2 + 1 + scroll + 0% + 0% + repeat + "Times New Roman" + 12pt + normal + normal + normal + black + none + none + none + none + none + none + none + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + 1 + 2 + scroll + 0% + 0% + repeat + "Times New Roman" + 12pt + normal + normal + normal + black + none + none + none + none + none + none + none + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + 1 + 2 + scroll + 0% + 0% + repeat + "Times New Roman" + 12pt + normal + normal + normal + black + none + none + none + none + none + none + none + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + + none + none + none + none + 0.5104166666666666in + + scroll + 0% + 0% + repeat + "Times New Roman" + 12pt + normal + normal + normal + black + none + none + none + none + none + none + none + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + scroll + 0% + 0% + repeat + "Times New Roman" + 12pt + normal + normal + normal + black + none + none + none + none + none + none + none + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + +
+ + + none + none + none + none + 0.3125in + + scroll + 0% + 0% + repeat + "Times New Roman" + 12pt + normal + normal + normal + black + none + none + none + none + none + none + none + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + 12.5pt + normal + center + stt + + + + none + none + none + none + 0pt + 0pt + 0pt + 0pt + + 12.5pt + normal + sp + + + + none + none + none + none + 0pt + 0pt + 0pt + 0pt + + 12.5pt + normal + center + donvi + + + + none + none + none + none + 0pt + 0pt + 0pt + 0pt + + 12.5pt + normal + + Custom + #,###.### + vi + + center + quantity + + + + none + none + none + none + 0pt + 0pt + 0pt + 0pt + + + none + none + none + none + 0pt + 0pt + 0pt + 0pt + + + none + none + none + none + 0pt + 0pt + 0pt + 0pt + + + +
+
+
+
+ + "Times New Roman" + 11pt + bold + 0.4166666666666667in + 7.510416666666667in + date + + + day + day + dataSetRow["day"] + string + + + month + month + dataSetRow["month"] + string + + + year + year + BirtStr.right(dataSetRow["year"],2) + string + true + + + + 1.2083333333333333in + + + 0.5833333333333334in + + + 0.3645833333333333in + + + 0.6354166666666666in + + + 0.20833333333333334in + + + 0.5in + + + 1.84375in + + + 0.6145833333333334in + + + 0.6145833333333334in + + + 0.4375in + + + 0.2708333333333333in + + + 0.22916666666666666in + + + 0.4166666666666667in + + + + 12.5pt + normal + 2pt + day + + + + center + + + + 12.5pt + normal + 2pt + month + + + + + + 12.5pt + normal + 2pt + year + + + + + + + + + + + + bold + 1.15625in + 7.5in + invoiceinfo + + + nhanvien + nhanvien + dataSetRow["nhanvien"] + string + + + nguoi_vc + nguoi_vc + dataSetRow["nguoi_vc"] + string + + + user_name + user_name + dataSetRow["user_name"] + string + + + + 3.3125in + + + 2.4479166666666665in + + + 1.7395833333333333in + + + 0.65625in + + + + + + 0.28125in + + + scroll + 0% + 0% + repeat + "Times New Roman" + 11pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + scroll + 0% + 0% + repeat + "Times New Roman" + 12.5pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + 2 + normal + block + auto + auto + auto + false + false + hidden + nguoi_vc + + + + scroll + url + 0% + 0% + repeat + auto + auto + "Times New Roman" + 11pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + 0em + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + scroll + 0% + 0% + repeat + "Times New Roman" + 12.5pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + user_name + + + + + + + + dlvlogo.jpg + + /9j/4RvZRXhpZgAATU0AKgAAAAgADwEAAAMAAAABAWYAAAEBAAMAAAABAEUAAAECAAMAAAADAAAAwgEG + AAMAAAABAAIAAAESAAMAAAABAAEAAAEVAAMAAAABAAMAAAEaAAUAAAABAAAAyAEbAAUAAAABAAAA0AEo + AAMAAAABAAIAAAExAAIAAAAcAAAA2AEyAAIAAAAUAAAA9AE7AAIAAAAFAAABCJydAAEAAAAKAAABDeoc + AAcAAAgMAAABF4dpAAQAAAABAAAJJAAAEcwACAAIAAgADqYAAAAnEAAOpgAAACcQQWRvYmUgUGhvdG9z + aG9wIENTNSBXaW5kb3dzADIwMTM6MDI6MjggMTE6MDg6MTcAVVNFUgBVAFMARQBSAAAAHOoAAAAIAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAmQAAAHAAAABDAyMjGQAwACAAAAFAAACZaQBAACAAAAFAAACaqSkQACAAAAAzk0 + AACSkgACAAAAAzk0AACgAQADAAAAAf//AACgAgAEAAAAAQAAAVqgAwAEAAAAAQAAADvqHAAHAAAIDAAA + Cb4AAAAAMjAxMzowMjoyOCAxMDo1NTo0MQAyMDEzOjAyOjI4IDEwOjU1OjQxABzqAAAACAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABgEDAAMAAAABAAYAAAEaAAUAAAABAAASGgEbAAUAAAABAAASIgEoAAMAAAABAAIAAAIB + AAQAAAABAAASKgICAAQAAAABAAAJpwAAAAAAAABIAAAAAQAAAEgAAAAB/9j/7QAMQWRvYmVfQ00AAv/u + AA5BZG9iZQBkgAAAAAH/2wCEAAwICAgJCAwJCQwRCwoLERUPDAwPFRgTExUTExgRDAwMDAwMEQwMDAwM + DAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBDQsLDQ4NEA4OEBQODg4UFA4ODg4UEQwMDAwMEREMDAwMDAwR + DAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIABsAoAMBIgACEQEDEQH/3QAEAAr/xAE/AAAB + BQEBAQEBAQAAAAAAAAADAAECBAUGBwgJCgsBAAEFAQEBAQEBAAAAAAAAAAEAAgMEBQYHCAkKCxAAAQQB + AwIEAgUHBggFAwwzAQACEQMEIRIxBUFRYRMicYEyBhSRobFCIyQVUsFiMzRygtFDByWSU/Dh8WNzNRai + soMmRJNUZEXCo3Q2F9JV4mXys4TD03Xj80YnlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vY3R1dnd4eX + p7fH1+f3EQACAgECBAQDBAUGBwcGBTUBAAIRAyExEgRBUWFxIhMFMoGRFKGxQiPBUtHwMyRi4XKCkkNT + FWNzNPElBhaisoMHJjXC0kSTVKMXZEVVNnRl4vKzhMPTdePzRpSkhbSVxNTk9KW1xdXl9VZmdoaWprbG + 1ub2JzdHV2d3h5ent8f/2gAMAwEAAhEDEQA/APVEkk6SnE+sP1jd0F9Fl+K6/DvlhtrcN7bB7tjqnhrH + NsZ9D9L/AIN6xL/rL0/604ud0pmPZS8Y5yMV9hbLrav0uzazd6fDPovf6lfrfzf+Esf4w/XyenY+Fi49 + 2Rc64XO9Ktzw1jW2Mlzq2u2ue9/s/trl+gdO6lguy+pXYmRWcfHezHaanhz77gaKWsYWhz2Ma6yy3Z/N + KrkyT93gGsK9Wn26u7yXKcqeRHME8HNRl+q9dcUxP9VL2/73ock9Y6s4MDs297a3Nexj7XuaHMIfW703 + O2+xzV6Blf4wMLCyXUZOJdq1llNlRa8PrsY26p/6U0bHe/a5nv8A664BvResOc1jcDILnENaDU8anQe5 + zQ1q0/rThZZ6n6VVFttWHRTii5tb9rjUxrXuDtv7+5QY8mWEJSF7xGov951+c5XkeZz4cZ4OEQyyl7co + 4+H+a4flfQfq79Ycfr2LZkU1updVYa31PIJj6VVnt/0jP/BPU/rrWXI/4vOmXYvTL8jIY6t2XYNjHCDs + rBa1+0+73WPtXTvw6njR1jP6lj2/gHbVexSlKETLSRGrynP4sWLms2PCbxwlwx1v+96v6sk6Wiz7OnZ4 + H6t1K6sjhtjKrWf2v0Vd/wD7MITn/WbHk+nidQYBpsc/Fs/zLPt1Tv8At6pSCN7Efl/0moZVuD+f5Osk + sV31moxZHVMPL6cGgbrbKvVp/wDYnBOTU3/rvpK7V1fp+VjOyMG5uc1o+jjOba4k/RZ7Xexzv+F9NjP8 + LsSMZAWRp3/R+1IkCaB17dW4ousA4lx8Bqsej60YRs25rHdPDyRV9pIY4gR/OM/we73O/wBH6Xp/pP0v + pq/T1bpdxayjKpsc4wxrXtJJlzfa2f3mOTRKJ6rzjnHeJZvOc/SoV1D96yXn/tqvY3/wdVn9O6jdPqdT + uqn83GrpYP8A2Yqy7P8AwVTZ1vprhkOFo24m/wBSNTFcNsexjN1jm+p+h+h+ls/mlVr+s+Daxzseu/Ic + 0uGymt1jgG7wx1gb/Neu6l/pb/8Arnpo8YHUI9mZv0y033CK36rWWSf211QO8r2Af5rKWLPyPqr9Z6ff + 0/r99hHFeSXf+fZvb/4Aum+244OzdNsSam+94/rMr3bVNpuf+b6TfOC78PY3/pqWOaY7H+9GJYZYcZ6E + f3TIPA3/AFl+unQrQzqtbba3GGvuY3Y7+TVlYmyvf/xjfU/4NdB0T67dM6pYzGuBwst5DWVWEFj3HhtN + 427nfyLPSs/cW9bj03VOpuY22p4h9bxua4Hs9r53Lhev/U0dOyaeodNBdhNuqN9BO51Q9Rn6Stx91mN/ + pGu/SUfT/mv5maMsGXScRiyfoyh8sj/dYZQz4iDCZy4/0oz+aMf7z//Q79/1m6ZXfdQ71d1LzWHem4ts + e19VFteO/wDwr6rsmmt//qK7YdvWsJ+WcNpd67bLKnNLSNvosrvtte76LaGsyMf9N+/kVMXK2/sz1Tx/ + Sbt8/b98/tKmP2X/AMN63/KH2f2ftD7Hv/UVp9N+y/trq/o+hv8As2Pv9f7R68ei30vt32v9Bs2/0j7P + +l/m/tn6VOPD0tGqLq131d65j1230ZZtf7cG2mmxl1rHh13qYjnN9O6j06n2/pv5r9z9Yx/VwXdG+qIe + Xt6hnGhlXq+ttBBHoftLY39E2zf9i/Te6n/gf5/9EtLH/ZX2PB/nvS26+n+0ftE+i3/k7/tV+yvQ+nt/ + R/zH/eiqr/sXv3fsyfs9n819r2bo6bu9b0/Z+xf5vZ/gPQ+w+j7PtSin924vXv8Ai6HKf6T9sfduP2ta + /c/rcPGvj9G+qQcHWW9QvDXFmTXZ9GmH/Zt2W/GYz063Xeze278y6z+axsh9Wt06/wCqGBXkWYeGWfYx + 6geanW2PAtsw2HHe717Xufk0WVVV+yz/AK2hZv2T7dR6v2f1vSwNno/afsv9Ju2/a/s36n6O7/kj1/8A + tb6/rfollH7D9krj9mTOPs9T7Z9n2fZLNn2vd/3sbPU9L7R+t/Y/Q9f9Y+yIw+7/AKNX9LWc1/pHhP3j + 3ODrxcXtf839W9ZZ9Zem1ZNmPaLGCosDry39FFo31Pbdu2ur9Jtlz3/4Kqp/q7FJn1n6WSxtpsxn2PDG + MvrdW6HV25VVux43Nqtqx7dn/C/q9n6f9Gufs+w/s98fYtu9set9r2T+yHf0jf8ApNn2b979H9l9X/vW + Vbqf7N+ydTj0fT9C3d+0/t/rbYwfU+3bv0/+h+yel+n/AO43/a5S+jxaWvg91jZDMnHqyKwQy5jbGhwL + XQ8b272H6LtURMOBxx24TpiVo8NFm5v1b6FnP9XIwqjcTJuYPStn/j8c13f9NaaSdDjv0cV/1f8A0FbP + gr11X9Zxx9XPTfuxup9QoHZhvF7fuz68tCH1ZyG3jIZ1J4tbG2w42IXgDdG1zcZm36b1upJ3r0vh/wAL + 2/8Au1o4aPDxV14Pc/Hgcir6v2M37s+79IIs2U4jA4S5218Ye53ue930laZ0nGH86+7IPf1rXuaf61O5 + tH/gSupJp4v/AEXh/wC4T6et/wDVOL/1IwqqqpYK6mNrYOGtAaB/ZappJJq9UpinSSU//9n/7RJMUGhv + dG9zaG9wIDMuMAA4QklNBAQAAAAAADAcAVoAAxslRxwCAAACB8kcAlAABFVTRVIcAjcACDIwMTMwMjI4 + HAI8AAYxMDU1NDE4QklNBCUAAAAAABC8gupWRsjz8GjDyu7wgYk9OEJJTQQ6AAAAAACTAAAAEAAAAAEA + AAAAAAtwcmludE91dHB1dAAAAAUAAAAAQ2xyU2VudW0AAAAAQ2xyUwAAAABSR0JDAAAAAEludGVlbnVt + AAAAAEludGUAAAAAQ2xybQAAAABNcEJsYm9vbAEAAAAPcHJpbnRTaXh0ZWVuQml0Ym9vbAAAAAALcHJp + bnRlck5hbWVURVhUAAAAAQAAADhCSU0EOwAAAAABsgAAABAAAAABAAAAAAAScHJpbnRPdXRwdXRPcHRp + b25zAAAAEgAAAABDcHRuYm9vbAAAAAAAQ2xicmJvb2wAAAAAAFJnc01ib29sAAAAAABDcm5DYm9vbAAA + AAAAQ250Q2Jvb2wAAAAAAExibHNib29sAAAAAABOZ3R2Ym9vbAAAAAAARW1sRGJvb2wAAAAAAEludHJi + b29sAAAAAABCY2tnT2JqYwAAAAEAAAAAAABSR0JDAAAAAwAAAABSZCAgZG91YkBv4AAAAAAAAAAAAEdy + biBkb3ViQG/gAAAAAAAAAAAAQmwgIGRvdWJAb+AAAAAAAAAAAABCcmRUVW50RiNSbHQAAAAAAAAAAAAA + AABCbGQgVW50RiNSbHQAAAAAAAAAAAAAAABSc2x0VW50RiNQeGxAWAAAAAAAAAAAAAp2ZWN0b3JEYXRh + Ym9vbAEAAAAAUGdQc2VudW0AAAAAUGdQcwAAAABQZ1BDAAAAAExlZnRVbnRGI1JsdAAAAAAAAAAAAAAA + AFRvcCBVbnRGI1JsdAAAAAAAAAAAAAAAAFNjbCBVbnRGI1ByY0BZAAAAAAAAOEJJTQPtAAAAAAAQAGAA + AAABAAIAYAAAAAEAAjhCSU0EJgAAAAAADgAAAAAAAAAAAAA/gAAAOEJJTQQNAAAAAAAEAAAAHjhCSU0E + GQAAAAAABAAAAB44QklNA/MAAAAAAAkAAAAAAAAAAAEAOEJJTScQAAAAAAAKAAEAAAAAAAAAAjhCSU0D + 9QAAAAAASAAvZmYAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAA + AQA1AAAAAQAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAA + AP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA//// + /////////////////////////wPoAAA4QklNBAAAAAAAAAIAADhCSU0EAgAAAAAAAgAAOEJJTQQwAAAA + AAABAQA4QklNBC0AAAAAAAYAAQAAAAI4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQQeAAAA + AAAEAAAAADhCSU0EGgAAAAADQwAAAAYAAAAAAAAAAAAAADsAAAFaAAAABwBDAGEAcAB0AHUAcgBlAAAA + AQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAFaAAAAOwAAAAAAAAAAAAAAAAAAAAABAAAAAAAA + AAAAAAAAAAAAAAAAABAAAAABAAAAAAAAbnVsbAAAAAIAAAAGYm91bmRzT2JqYwAAAAEAAAAAAABSY3Qx + AAAABAAAAABUb3AgbG9uZwAAAAAAAAAATGVmdGxvbmcAAAAAAAAAAEJ0b21sb25nAAAAOwAAAABSZ2h0 + bG9uZwAAAVoAAAAGc2xpY2VzVmxMcwAAAAFPYmpjAAAAAQAAAAAABXNsaWNlAAAAEgAAAAdzbGljZUlE + bG9uZwAAAAAAAAAHZ3JvdXBJRGxvbmcAAAAAAAAABm9yaWdpbmVudW0AAAAMRVNsaWNlT3JpZ2luAAAA + DWF1dG9HZW5lcmF0ZWQAAAAAVHlwZWVudW0AAAAKRVNsaWNlVHlwZQAAAABJbWcgAAAABmJvdW5kc09i + amMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9t + bG9uZwAAADsAAAAAUmdodGxvbmcAAAFaAAAAA3VybFRFWFQAAAABAAAAAAAAbnVsbFRFWFQAAAABAAAA + AAAATXNnZVRFWFQAAAABAAAAAAAGYWx0VGFnVEVYVAAAAAEAAAAAAA5jZWxsVGV4dElzSFRNTGJvb2wB + AAAACGNlbGxUZXh0VEVYVAAAAAEAAAAAAAlob3J6QWxpZ25lbnVtAAAAD0VTbGljZUhvcnpBbGlnbgAA + AAdkZWZhdWx0AAAACXZlcnRBbGlnbmVudW0AAAAPRVNsaWNlVmVydEFsaWduAAAAB2RlZmF1bHQAAAAL + YmdDb2xvclR5cGVlbnVtAAAAEUVTbGljZUJHQ29sb3JUeXBlAAAAAE5vbmUAAAAJdG9wT3V0c2V0bG9u + ZwAAAAAAAAAKbGVmdE91dHNldGxvbmcAAAAAAAAADGJvdHRvbU91dHNldGxvbmcAAAAAAAAAC3JpZ2h0 + T3V0c2V0bG9uZwAAAAAAOEJJTQQoAAAAAAAMAAAAAj/wAAAAAAAAOEJJTQQRAAAAAAABAQA4QklNBBQA + AAAAAAQAAAACOEJJTQQMAAAAAAnDAAAAAQAAAKAAAAAbAAAB4AAAMqAAAAmnABgAAf/Y/+0ADEFkb2Jl + X0NNAAL/7gAOQWRvYmUAZIAAAAAB/9sAhAAMCAgICQgMCQkMEQsKCxEVDwwMDxUYExMVExMYEQwMDAwM + DBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQ0LCw0ODRAODhAUDg4OFBQODg4OFBEMDAwMDBER + DAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAAbAKADASIAAhEBAxEB/90ABAAK + /8QBPwAAAQUBAQEBAQEAAAAAAAAAAwABAgQFBgcICQoLAQABBQEBAQEBAQAAAAAAAAABAAIDBAUGBwgJ + CgsQAAEEAQMCBAIFBwYIBQMMMwEAAhEDBCESMQVBUWETInGBMgYUkaGxQiMkFVLBYjM0coLRQwclklPw + 4fFjczUWorKDJkSTVGRFwqN0NhfSVeJl8rOEw9N14/NGJ5SkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2 + N0dXZ3eHl6e3x9fn9xEAAgIBAgQEAwQFBgcHBgU1AQACEQMhMRIEQVFhcSITBTKBkRShsUIjwVLR8DMk + YuFygpJDUxVjczTxJQYWorKDByY1wtJEk1SjF2RFVTZ0ZeLys4TD03Xj80aUpIW0lcTU5PSltcXV5fVW + ZnaGlqa2xtbm9ic3R1dnd4eXp7fH/9oADAMBAAIRAxEAPwD1RJJOkpxPrD9Y3dBfRZfiuvw75Yba3De2 + we7Y6p4axzbGfQ/S/wCDesS/6y9P+tOLndKZj2UvGOcjFfYWy62r9Ls2s3enwz6L3+pX6383/hLH+MP1 + 8np2PhYuPdkXOuFzvSrc8NY1tjJc6trtrnvf7P7a5foHTupYLsvqV2JkVnHx3sx2mp4c++4GilrGFoc9 + jGusst2fzSq5Mk/d4BrCvVp9uru8lynKnkRzBPBzUZfqvXXFMT/VS9v+96HJPWOrODA7Nve2tzXsY+17 + mhzCH1u9Nztvsc1egZX+MDCwsl1GTiXatZZTZUWvD67GNuqf+lNGx3v2uZ7/AOuuAb0XrDnNY3AyC5xD + Wg1PGp0Huc0NatP604WWep+lVRbbVh0U4oubW/a41Ma17g7b+/uUGPJlhCUhe8RqL/edfnOV5Hmc+HGe + DhEMspe3KOPh/muH5X0H6u/WHH69i2ZFNbqXVWGt9TyCY+lVZ7f9Iz/wT1P661lyP+Lzpl2L0y/IyGOr + dl2DYxwg7KwWtftPu91j7V078Op40dYz+pY9v4B21XsUpShEy0kRq8pz+LFi5rNjwm8cJcMdb/ver+rJ + Olos+zp2eB+rdSurI4bYyq1n9r9FXf8A+zCE5/1mx5Pp4nUGAabHPxbP8yz7dU7/ALeqUgjexH5f9JqG + Vbg/n+TrJLFd9ZqMWR1TDy+nBoG62yr1af8A2JwTk1N/676Su1dX6flYzsjBubnNaPo4zm2uJP0We13s + c7/hfTYz/C7EjGQFkad/0ftSJAmgde3VuKLrAOJcfAarHo+tGEbNuax3Tw8kVfaSGOIEfzjP8Hu9zv8A + R+l6f6T9L6av09W6XcWsoyqbHOMMa17SSZc32tn95jk0Sieq845x3iWbznP0qFdQ/esl5/7ar2N/8HVZ + /Tuo3T6nU7qp/Nxq6WD/ANmKsuz/AMFU2db6a4ZDhaNuJv8AUjUxXDbHsYzdY5vqfofofpbP5pVa/rPg + 2sc7HrvyHNLhsprdY4Bu8MdYG/zXrupf6W//AK56aPGB1CPZmb9MtN9wit+q1lkn9tdUDvK9gH+ayliz + 8j6q/Wen39P6/fYRxXkl3/n2b2/+ALpvtuODs3TbEmpvveP6zK921Tabn/m+k3zgu/D2N/6aljmmOx/v + RiWGWHGehH90yDwN/wBZfrp0K0M6rW22txhr7mN2O/k1ZWJsr3/8Y31P+DXQdE+u3TOqWMxrgcLLeQ1l + VhBY9x4bTeNu538iz0rP3FvW49N1TqbmNtqeIfW8bmuB7Pa+dy4Xr/1NHTsmnqHTQXYTbqjfQTudUPUZ + +krcfdZjf6Rrv0lH0/5r+ZmjLBl0nEYsn6MofLI/3WGUM+IgwmcuP9KM/mjH+8//0O/f9ZumV33UO9Xd + S81h3puLbHtfVRbXjv8A8K+q7Jprf/6iu2Hb1rCflnDaXeu2yypzS0jb6LK77bXu+i2hrMjH/Tfv5FTF + ytv7M9U8f0m7fP2/fP7Spj9l/wDDet/yh9n9n7Q+x7/1FafTfsv7a6v6Pob/ALNj7/X+0evHot9L7d9r + /QbNv9I+z/pf5v7Z+lTjw9LRqi6td9XeuY9dt9GWbX+3BtppsZdax4dd6mI5zfTuo9Op9v6b+a/c/WMf + 1cF3RvqiHl7eoZxoZV6vrbQQR6H7S2N/RNs3/Yv03up/4H+f/RLSx/2V9jwf570tuvp/tH7RPot/5O/7 + Vfsr0Pp7f0f8x/3oqq/7F7937Mn7PZ/Nfa9m6Om7vW9P2fsX+b2f4D0PsPo+z7Uop/duL17/AIuhyn+k + /bH3bj9rWv3P63Dxr4/RvqkHB1lvULw1xZk12fRph/2bdlvxmM9Ot13s3tu/Mus/msbIfVrdOv8AqhgV + 5FmHhln2MeoHmp1tjwLbMNhx3u9e17n5NFlVVfss/wCtoWb9k+3Uer9n9b0sDZ6P2n7L/Sbtv2v7N+p+ + ju/5I9f/ALW+v636JZR+w/ZK4/Zkzj7PU+2fZ9n2SzZ9r3f97Gz1PS+0frf2P0PX/WPsiMPu/wCjV/S1 + nNf6R4T949zg68XF7X/N/VvWWfWXptWTZj2ixgqLA68t/RRaN9T23btrq/SbZc9/+Cqqf6uxSZ9Z+lks + babMZ9jwxjL63Vuh1duVVbseNzarase3Z/wv6vZ+n/Rrn7PsP7PfH2LbvbHrfa9k/sh39I3/AKTZ9m/e + /R/ZfV/71lW6n+zfsnU49H0/Qt3ftP7f622MH1Pt279P/ofsnpfp/wDuN/2uUvo8Wlr4PdY2QzJx6sis + EMuY2xocC10PG9u9h+i7VETDgccduE6YlaPDRZub9W+hZz/VyMKo3EybmD0rZ/4/HNd3/TWmknQ479HF + f9X/ANBWz4K9dV/WccfVz037sbqfUKB2Ybxe37s+vLQh9Wcht4yGdSeLWxtsONiF4A3Rtc3GZt+m9bqS + d69L4f8AC9v/ALtaOGjw8VdeD3Px4HIq+r9jN+7Pu/SCLNlOIwOEudtfGHud7nvd9JWmdJxh/OvuyD39 + a17mn+tTubR/4ErqSaeL/wBF4f8AuE+nrf8A1Ti/9SMKqqqWCupja2DhrQGgf2WqaSSavVKYp0klP//Z + ADhCSU0EIQAAAAAAVQAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABMAQQBk + AG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAFMANQAAAAEAOEJJTQQGAAAAAAAHAAQAAAABAQD/ + 4Q5SaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1 + TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEv + IiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjAtYzA2MCA2MS4xMzQ3NzcsIDIwMTAvMDIvMTItMTc6 + MzI6MDAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8w + Mi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnht + cD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9k + Yy9lbGVtZW50cy8xLjEvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21t + LyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2 + ZW50IyIgeG1sbnM6Y3JzPSJodHRwOi8vbnMuYWRvYmUuY29tL2NhbWVyYS1yYXctc2V0dGluZ3MvMS4w + LyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bXA6 + Q3JlYXRlRGF0ZT0iMjAxMy0wMi0yOFQxMDo1NTo0MS45MzgiIHhtcDpNb2RpZnlEYXRlPSIyMDEzLTAy + LTI4VDExOjA4OjE3KzA3OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDEzLTAyLTI4VDExOjA4OjE3KzA3 + OjAwIiBkYzpmb3JtYXQ9ImltYWdlL2pwZWciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6M0I1QTAx + RjY1QTgxRTIxMUJEQzFEODVDQUJCNEIwNjgiIHhtcE1NOkRvY3VtZW50SUQ9InV1aWQ6ZmFmNWJkZDUt + YmEzZC0xMWRhLWFkMzEtZDMzZDc1MTgyZjFiIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InV1aWQ6 + ZmFmNWJkZDUtYmEzZC0xMWRhLWFkMzEtZDMzZDc1MTgyZjFiIiBjcnM6QWxyZWFkeUFwcGxpZWQ9IlRy + dWUiIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpEYXRlQ3JlYXRlZD0iMjAxMy0wMi0y + OFQxMDo1NTo0MS4wOTQiPiA8ZGM6Y3JlYXRvcj4gPHJkZjpTZXE+IDxyZGY6bGk+VVNFUjwvcmRmOmxp + PiA8L3JkZjpTZXE+IDwvZGM6Y3JlYXRvcj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxp + IHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6M0E1QTAxRjY1QTgx + RTIxMUJEQzFEODVDQUJCNEIwNjgiIHN0RXZ0OndoZW49IjIwMTMtMDItMjhUMTE6MDg6MTcrMDc6MDAi + IHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDUzUgV2luZG93cyIgc3RFdnQ6Y2hh + bmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9Inht + cC5paWQ6M0I1QTAxRjY1QTgxRTIxMUJEQzFEODVDQUJCNEIwNjgiIHN0RXZ0OndoZW49IjIwMTMtMDIt + MjhUMTE6MDg6MTcrMDc6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDUzUg + V2luZG93cyIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9y + ZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+ICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVu + ZD0idyI/Pv/uAA5BZG9iZQBkAAAAAAH/2wCEAAYEBAQFBAYFBQYJBgUGCQsIBgYICwwKCgsKCgwQDAwM + DAwMEAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBwcHDQwNGBAQGBQODg4UFA4ODg4UEQwMDAwM + EREMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIADsBWgMBEQACEQEDEQH/3QAE + ACz/xAGiAAAABwEBAQEBAAAAAAAAAAAEBQMCBgEABwgJCgsBAAICAwEBAQEBAAAAAAAAAAEAAgMEBQYH + CAkKCxAAAgEDAwIEAgYHAwQCBgJzAQIDEQQABSESMUFRBhNhInGBFDKRoQcVsUIjwVLR4TMWYvAkcoLx + JUM0U5KismNzwjVEJ5OjszYXVGR0w9LiCCaDCQoYGYSURUaktFbTVSga8uPzxNTk9GV1hZWltcXV5fVm + doaWprbG1ub2N0dXZ3eHl6e3x9fn9zhIWGh4iJiouMjY6PgpOUlZaXmJmam5ydnp+So6SlpqeoqaqrrK + 2ur6EQACAgECAwUFBAUGBAgDA20BAAIRAwQhEjFBBVETYSIGcYGRMqGx8BTB0eEjQhVSYnLxMyQ0Q4IW + klMlomOywgdz0jXiRIMXVJMICQoYGSY2RRonZHRVN/Kjs8MoKdPj84SUpLTE1OT0ZXWFlaW1xdXl9UZW + ZnaGlqa2xtbm9kdXZ3eHl6e3x9fn9zhIWGh4iJiouMjY6Pg5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra + 6vr/2gAMAwEAAhEDEQA/APVGAlFN0GG1p1MbWmu9MU06orjaKWh1NaHYdciSyqnVWuSpjxhTnSOSNo5B + yVgVZT3BFCMBBI2UZDHcPkvzVH5g8i+c9QsdNv7iyjWT1LUxSMA0MnxR/DXidvhauaDUZc2KdX9z7H2V + hwdo6KEsseOvTLn/AAf1eBXj/O38y0h9IaqGFKc2ghL/AD5cOuRlrshDP/Qh2fuYY+f9PL/vppr+T3nT + U/8AlZMUuq3kl1Jq8bWkkkhr8f24h/krzG3H+bLdJq8hluXWe0vY2PFof3Y4fDkJf5tS/pPpxWpXc7f5 + 9Tm9JfKx323yxpPEGuRJ9qYghBu/JsEkdcJC3fJcMiyp1MbRTgMbWnYq6mG1p1MFrTsCXYq7CrsVdirt + 8Vdirt8VdirRbFVjSqgLOQqgVJJAFMHCTyUkBItQ8/8AlKwYpNqUTyj/AHTBWeSv+rEHOWRxSLTLPEJP + c/mdMwP6K8s6xqPg31YwIfcGUqaf7HLI4R/EeFqOpJ+kWlN358/NiXew8lNED0NxOhP0qOP68yYYMHWX + 2ONPPn/hj9sUmu/OH5/CpXy9DEPBEWQj75f4ZeMGl/nf7px5ajVDp/uEivvzG/PW0Ba5sJIF8fqHIfeK + j8cyIaPSy5S/3f63HnrtUOn+4/UlY/Pj8x4HKSy2zOOqSWwQ/dyU5b/JWA8j9kv+KaT2rqBzH+5/4lML + T/nI3zbHT61YWU478OcR+8lxhl2JjPKX2f8AHmY7ZyDmPx/pWS6Z/wA5I6RIwXUtKuLcftSQuswH+xoj + ZhZuyJRPp9X4/rOXi7YiR6vx/sWc6F+Z/knWyqWeqxLO3SCasL18AJAvL/Y5rsujyQ6Owxa7HPqydZQR + Ubg9DmNRHNy4yEuS7nUbbYxNpkKa5N4/s1+nLKDCz9j/AP/Q9Ud8CWxhYh2KXYq0ymmxofHEqNnhP51D + zF5Q1u28waBqVxaW2pkpeQK3KEToNm9NuSfvF/yf2M1uunPH6onZ7f2W02m1Ylhyw4pg8XHxS/0vDFhS + /n1+ZYUKb6A7UJNtHU5g/wApZa5/d+p6n/QZpL5f7v8A6qKnkv8ANvzNb+drfUNXvWurW+dLe9iYBY1j + ZqK6Ko+H0yeWHFrshmLLR2r7LYBppDF6JQvJ/FL6Y/R6p/xMi/5yV0uKPVdH1NFo9xDJDKfH02DL/wAT + y/tQEAbur9hNTMmcL9I/heL/AKv8zmnsvpQFI7Q759P1qwvo24Na3EUwbwKOD/DLsEyJCnX9o4eLT5I/ + zsc/9yXrX5o+YvN3kjziJtE1KWPTtVhF5HbTBZokdjSRVDhtqjl/ss2mr1GTGRR6PCezvZuDX4ZDLG8m + KXBxer6f8wwY7/yv78yfTKm6tiT0f6utR+OYv8pZfxX6ncx9itLd/wDF/wDVRMPy8/N/zK/niA65efWb + LVJI7eVGASOJiaI0YA+H4j8WW6fXSkak4PbnsvhxaWU8fp8McXX1f6ab6UA2H35uwdny+IoL12Br33xR + EFuuC2VNVwq3XFFtcsVbrgVwNcK27FXYpdirq4odXFXVxS0XCip2HicVQ8l0SaQxtM3tsv8AwRoMBC2o + SQarPsZ0tFP++1Ej/e/wj/gMkCxItCv5U0mZuV8j37Vr/pTtIn/Isn0v+SeHjPRh4YPNMLfT7O2ThawR + 26dOMShB9w2wGR72Qxx7lYR0wWU8Ee5cF264ppvjgquSrShJx3XhCX6j5e0XUlKahYwXSkUIljV/1g5Z + DLOPVpyafHLmGDa7+Qnku/DvYCXS7g9DA3OOvvG5O3+rxzOw9ozgd93X5uyMcuW3z/4p5X5r/JjzjoYa + aBF1WzXcyWwPqBfeI7/8CWzcaftUT5+n8f1XT6nsrgPp3/H9ZgDIVLJIvF1PF0YUII7MKBh9ObIGMw60 + iUCyjyx+ZXm7y46ra3zTWgoDZ3NZYqDstd0/2JzEz6CGQ77FysGvnDlye5eRPzk8v+Y3jsbr/cbqzCiW + 8p/dyN4RP3/1T8Wc/q+z5YjY9UXo9H2lHIKPpP48nodB/wALmA7Ow//R9UYClvCh2KuxV2KvNvz+tYp/ + y7uncDnBNDJEx7Nz4/iDmFrx+6L0vsjkMdfEDqC+W/47/fnOnk+0jk3GrM6qlebEKtOtTsPxOSh9QcfU + yAxSJ5cJ+57f/wA5FzKuk+WbV/74q7se4ARFP4nNt2odovnnsTj/AH+aQ5f8eeHds03V9MHJcoqQMsw/ + WHG1n91P+pL/AHL2r/nI9EEPllj/AHvpSg/6tIyc22v5D+q+fexUj42TueJ5pH0mXT3ojT+f1234V5+r + Hxp15chxp9OW4PrDidoV4E75eHL/AHL7ciFwbdApCycRUsOQr36EZ1keT8+ZPrNclCQ60pJjFtL7Hmh+ + /wCPDVoMqQsup+Y4RU6Qs4/4ouUr90qx/wDEseANZmUFN54+q1+u6JqcAH2nSATqP+RDSN/wuTGInkwO + YDmoR/mt5EL+nNqi2snQpdJJAR8/UVcP5eaPzME9sfMGiagA1jfW90D0MMqP/wARJys4pA7hmM0D1R/q + LT28emNMxO+Tg4pUdMiQWd964HIhW8mrsVaYiuKOKmiwH+ffBaSXA1FOnuMKAVv1dK1arH/K3xSvCgdP + u7YodsMVdUHFLjtirgwxVuoxVwxV2+KtFqdcVUpJ40NCRyP2VG5+7CrSiR2JYcR4d/vyJJ6LwhiHnP8A + K3y15njeSWH6pqJG17AArk9uY25rvvXMvS66UC4Gq0EZh89ec/IeveVLr07+P1LSRqQ38VTE47D/ACT/ + AJJ/4bOn0uthljR+p5jVaGeI2PpY6C6MpBKspDKy7EMpqCCOmZmSJEd+Th4jcrHN7p/iHW/+W2X/AMlz + +kOp/wB6/wDf3/GT3zluGP8A0v8A9i9bxS/6Uf7J/9L1RgKW8KHYq6u+KGi1Po3OJS8X/wCcjfNdtFo9 + r5bikBu7uRZ7lO6wx1K18ObZre0Mw4eE9Xt/YvQZJZ/Gr93D08X9J89ZoX1cE3VbMu/Kzyy/mDzpYW5T + na2si3d4eyxRENv/AKzcVzL02EzN9zzXtT2hDDpZQ/jyeiP4pMfzr81Qa/50lS1kEljpifVIWXdSwNZC + P9l8P+xy7tHIZSArk4/sfoTh0vGfqyniYCema4inrQSn3kPQJ9f83aZpsSckedHuD1CwoeTs1P8AJXj/ + ALLMjTY+KTpe3taNNpZzI5x4P9OCzf8A5yJ1qG8812emQtVNMtgsg8JJTyI+hAmZfaU/UB/Ref8AYjSG + OCWU/wCVn/sdnlOat7sbBmX5R+XH13zxp8fDnbWUgu7uvQLEaqD/AKz8cztDi4pvL+1Wvjh0Ux/Fk9If + XCmv3Z0Z9L4uDcbXgbYqHFARQ4pWmOvfAb70GIPRD3Wl6fdpwu7aK4U9VlRXH/DA5ISkOrA4onoxXU/y + g8g3rFxpi2k7b+vaO8Dg+3AgZkw1eSI2O3wcfJpISPJKJfyu8z6aOflzzffW1Ps297S4j+VW/plo1UJf + XHi/zmg6ScfplX+ah31f879EBa70uz1+2XrJan05SB/k1/40yQx6eXI+F/R9WRHiaiPMeL/S9ONu0/Pj + RYZRB5g0q90afoxljLoD8wA3/C5CWhJ+k8X+xWHaFH1x4fjxM00Xz55R1kD9G6rbzsf918wr7/5DUbMf + Lp8kDuHMhqsc+RTz1R1pt49spsN4JSTzN5rstEijLQTXtzM6pHY2oWSdg3VglQeK/tNkJmm/BgOQ7cmD + Rfmzc3+qhoYH0/TbFyL2KeBzcSyVKJbR9F9Uk8nX4uC5R+YDs59mmMbu/wAe9nGheZYL23H1oxWuoIK3 + Vj6qs8JI5BX+z8XD4m/ly6GQEOsy4+Ep3661C7cjvxBr2rk7abDhOp6d+nv8qYbCkbWEu17zDY6LYPe3 + pKxrsqKOTu52CIo+0zZGUwGeLFKaKtLwT28UrI0UkiK7QPTmhIBKsB+0OmGJsWxMJDoxzzD+Y+g6al9b + 28q3mq2YjT6jGQWM87FYoix+AMSrc/i/dovN/hyJm5OLSTmLb1H8xdB0m1H1+b1LtIUlnitEecLz40AK + AjfmCv8AMnx5E5gOaMelnJrS/P1rqWtx6dawN6JjVpp5CUaOWSMSpEUK7twb46N8GMcwLLJo5RjZZWJB + T/PplrhWgZ9d09JTBG5uLgf7ogBkf6eOy/NiuGl4nL+k7n7dLOI/sgh5T93wr/w2K80Vb2cUO6j4j9pj + ux+ZwKArgU6YsmiMFKg9V0qw1Ozlsr+Bbm0mXjJE4qDX9RHiMnGRjuObXkgCN3zd+Z35XXflSY39kWuN + Cmeiud2gJqeL0/Z2+F/9j/rdL2X2h4kTCfT8dzzHaXZwhLjj+PtZR6b/AMrf+Sw49B18Ov2sweEf9fLs + eI/9ez//0/VGBLeFDsVccVWt1/ViUDm8M/NeDU9H1WfVrnynpmr6dcEf7kmSVpFoKATAP8NB+19nNdq5 + iPMW9h7P4o5z4YynDPpHg43nQ/MHR6/8ofo59ysh+X7XhmvjrIj+H7S9cfZucjXjy/5VxTDS/wA4ZNJj + uI9M8t6bZJdLxn9ESKXWhFCeVab5Ia+N3w/7Joy+yJmbyZieH6f3Y/4pKR520Lv5P0rbsWnP/G+QyamE + j9H+ycyHs/qKrxzH/klD9a4+d9CI/wCUO0r75/8AmvJDWQG3D/skD2f1A3/MH/lTD/ik28s/mjeaXdvL + 5d8qWMV3KvpubZJ3dlrXjsS3XLMerP8ADH7XXa72bE8d58xof7X/ANUyn1poHnDzPez6hP5AsfWunMk1 + 1eTTw8mbqSPV5f8ACZkGEsu8of7J1ctVi00BCGq+kbR8D9jL9J/Jq2kkrq+i6PBHT7Nt9bkevhVpVGXR + 0sR/B/snT5faLUg+jIZf8k8cf0M78s+S/LnlxJv0RYx2hn4+uU5Hlx6faZj38cyMeERNgcLqNd2hn1Ve + KeKv6v8AveFPFG2XkODIUKXqKDAlvFXYq7FXHFFtU98CbWlfiqOuJlSN0Jf6Vp2oIYr61iuYz+xKiuPx + GWQykciwngjLmwXXfyK8kaiTJaQyaXP1WS2c8QfHgxK5m4u0MkBRNh1+XsyBNgV+Pex0fl1+bflw8vLf + mH69br9m2uGC1H+q/NP+GXLhqMGT6xX+m/3rT+Wz4voN/wCl/wB8pr5//MfQ7t5/MHlEXDlRHJfWqMjs + gNac0Eo413wHs/TT3hL7JLHX6iB/eDf/ADVG2/M38smjt7Wewv8AQWt7o3qrGD/fMGDMSpL0bm/LbMWf + ZdfTv+Pe5ke3wD6vx/sW7O1/KW9uHki81PGslSYZiEYOXEhJdlDfEwHqcvikX4H+HMbJ2ZIncfj5u0xe + 0GIx2/33/EombQLSCLUdRs/NNveXEtosTSwyB7mXi/ORFBl2eZVVV3+H9jhmNLSSj0cvH2vhl0/3X/Ep + j5c8ueaBLpU19qtsYbeQXcsZuWZ45ODJ6dAeMkYQoAvw8XVvtfayMMEr7k6jV4pj0hPfOflyfXNR02aD + U4bWC0DcpedXjYuhLxqNmd41eOrH4OXL7WSngPe42n1scfRT8r20Oh3d9ea1rNlJcXLkGYTAFow7GPkG + IWPgrU4J/wAHlkdNIjb7mOq12ORH8OzFb6y8iOlzHqvmyCSWW4u54xZqpb/SgyhnEYcySRq3FT/Kv+U2 + H8jNnj7b8MVH8f7FMtKTyukt0+l6Xq+sJeLweMwmK3J9NYiweUQ8W9NafCeKfsKuS/JgfU0T7YyZN6/H + yZBpmj64jLJp2i6foLemsPryMbm59Jeg+AINv8qV8lHFji0S1WWf4CdR+WXn31W/uL7xhDehb/8AIqKl + R/rs+SMgeSAD1Ta2sbW2iEVvCkMY6JGqqPuAplZLYArgbYAlvCrsVdirTA/0wdVQeq6ZaalYT2N5GJrW + 5jaKaJuhVgQRkvEMCCGvLiExTFv+VZ6b/wAtM3/KN/4Z6j+4/wB+/wDGTLvFP+z42Hgj/YcD/9T0vqfm + LQtJaNdT1G2sWlr6YuZo4iwB348yK4xFqFWXWtKhsP0hLeQR2FAfrjyIsNCaA+oTx3O3XCBuxBXz6rp9 + v6AnuYoTdMI7USSKvqORUKlT8TH/ACcA5smrjUrGC4hgmnjjnuOQt4XdVeQqKtwUmrcRu1MQWI5rNQ1b + TNNgE+pXcFnCSVEs8iRqT4AsRvjG5HYJmQ5bvT7uy+spPFPYyJyE6urRMlOoYfAy5EjoQsZVuDuxBPJX + 5SeY7iWa2s9Ov50r6xtZa8TU/aELim/+TkZaYA7h2mm7b1OEVCdf5sf+JSC4/Ln8iTfNbNcWkV3z4NbC + /KsH6ceHqVDZTLQ45bkOVH2o1g24/wDYQ/4hG3H5bfkrojJHqUdpbSSjnGl7eFWYeKh5BUbYY6DH0jfz + XL7Ua6X+U/2GP/iE1s/y9/Kq3tP0nDplg1oBzW8ZhJDxG1Q8jNHko6XHHkHEy9uanIKlP7I/72LINBuP + LMkLtoU1nLboeLmyaNlUjxMZpl9UBQdbLNOZ9UuJbb+cPKc84todbsJbhmKiFLmJmLA0pQNWuGiAWIlu + mGoapp2nQC41C6hs4CyoJbiRYkLN9lQXKjk3YYBakhC6h5p8tadOsGoapaWk7r6iQzzxxuUPRgrEErt9 + rDEE2mwGrTzV5auzGLXVrO4M8hhgEU8T85AORReLGrgfs/awAGmIKpP5l0C2vVsLjUrWG+enC1knjSU1 + /wAhiGwgFlxBEz6pp8F1DaTXEUd1ccjb27uqySBPtcFJBamCithuLUrKa5ntYp45Lq24+vArhnTkKrzU + bry98jabCIBrhtXEn5Y8QS6uPEFa2rjxhBAd9OCwilpjUty74CIE2mz3tlQe+S4gtBwXwO2DboqEu9E0 + i8BW7soLgHqJY0f9YywZZjkWuWGMuYSO7/K78vrokzaFa1PdE4f8Rpl8dfliK4vsDTPQ4pcx9skrm/JD + 8t5WDDTDER3jllU/8SyX8o5O/wCxq/k3F3f7r9al/wAqJ/L0EcbWdVH7K3EwH/EsI7TyDr9iP5Mxk3+P + vVY/yQ/LqMg/o93p/PPM3/G2CXaWU/xfYGZ7Nw932y/Wj7X8p/y6tiDHoVsSOnqKZaf8GWyuWsnLmfsZ + R0GIch/uk9s/L+iWIAsrG3tqd4okT9Qyg5SereMURyRwRRkeLzbCLboMbim2xt0xsK3XHiCHcseIK7lh + 4gruWPEFdyx4grupwHfkq0qT32x8ljtu3xH/AAtMmh//1elarP5Z1384bz9NtaSaV5e0r0THemMwm5nl + Jb4ZPhqsY3/l+HMwAjFt3uOZblhbtZy+W7jS7Ei28s+ZPNcUWkRyH04vqMBVrh4wxFIeca8csrcb/wAD + CMnon5xXVk/lK01qwniuD5e1KzvS0LLIFCShW3HLj8L/APA5RgiTPdsMkT5FQ+afMV557uEJsVDWHliN + wTxtkP725AP7VxIDRh/uteODMREcPVMBZtJ5R5d1P82teHm6S3aDRbW3Gj2d+y+gI5F5Szqkp4u1fhZv + iyQJEPSsxuk2tf4NbRtAsNEmmsvI/mHWpX1WVy8MHGMV9GPlx9K2nkHEcfg+HLISNni+rh9KTFMdUj8p + 2P5k6JN5Vgtrc6PZ3dz5hmsQixLaLCfSjmaP4WcyD4OTcsgDMxPFytrlFgUGmag3k7TxqekaTBaecrmX + j5pu1aS5tpLpyyepVF9MtTjA6ysv/B5kSmLBB5D6WHCWVXGk6vf/AJhXttpmjWXmSDy1ptnpP+5SUJGs + hHqNJx4yhn7N/wASysy2u+G08JUv8Ix6JqXlTyh5muYl0XULi/1a9hiJjspLoFTBaKW4j00HxBG48sjx + k2R3J4KRmo32gaV571nWPKccFvpui6Dcfp2WyAW1e6Y1tIqRj0zMPtHj/ssYk0Ae9NMWtvK98NG8r+Vd + V0vStJXXljmh80MrS3ZfkLkxciqGK5YHiB6nHLSb4jaBYKcfmhq8Hmq/1WwNtf3ei+XraaGxmtLeWeKX + VlT4pJZY/gVbYDj8R+03LIY4UB5lE5G0187xaR5o8seRAbS3n1LzHPYxyXbIry/V4k9WYB6cuFeuMJEG + Y/oSZ1aK8x2Nnb/mLbWeh2MMEflrSL3V/qsEaqpvJ1MUTMq8QX+EFT9rIwJMBfWQUxSBLf8ALtfybfUr + 02upeZdYgaRrl2SW/k1GWpCq3xSqyPT4V+zkpylx+UfUvCo6/Y+ZrvW7a+jkkfWPIOi2N3PGN2kuXYSz + RNWv27dW/wBbJwl/0s/3zXKJZ7+S8ya1F5h83KpVNe1Fmtyw+L6vboscak+3xZjaqxIC+QbsMdnpQFMx + qLdTeELTsUcLsV4XYrwuxXhdivC6mBNNUxorTeFadijhdimnYrTsVp2KOF2K8LsV4XYrwuxXhdivC7Fe + F2K8LsV4XYE06mNFadT9WFFP/9aaecfq/wDivUK/8q/5eu1f0v6/12tf930+HnmTivhPP/YtBqzy/wBk + yT8wvR/w/ovqf4U+y1P0x6v1P7C/7x+n2/41xx3f8X+xRt5f7JAeWPQ/wH5h/wCUM4ck5foz1v0b0H+9 + 37fL+Xjg34uv+xZbfi3pPlnj/h/T6fU6ehH/AMc6v1T7P+6K/wC6v5Mpn9SY08//ADe+r/pOw9X/AAl/ + djj/AIj9b6xXl+x6X+6f9f4eWZGC+nF/sf8AfMZVfT7WQ+auH/Kv/i/QPH00r+kOX6Jpv9nj8XH+SmV7 + 8fX/AHzI0kP5feh/gjV+H+E/T9J6foX1fqNOB/3t5/vKfzftenl2W7H1f7FG3km+tel/yrK2r/h7h6Ft + /vV6n6GpVf7unx8P98f5XHKoXx9f9ig15famPlD0v0tr3D9Ff70x1/R/qfWP7of728/93fyU/wB1ccc9 + 2Lv/AGKY15fagfzc9L/DR9T9A8fUHL/EvqfVen7Hp/F6v+pgxXvz+xM6SjSPR/5VTeU/wp6fp7fV/V/Q + lKr/AL0V/eff/k4Tdjn9jHby+1O/OHp/oDR+X6Dp9YtqfpTn9U+z/wAenHf1f98f5OMb35/7FIr8Wr+U + vT/wfLx/Q/GtzT9Hep9Q+0397y+Kv/LRy/ysjvY5qa/FpbYelx8l0/w70fj6fqU/uz/xya/8Pz/YyW9n + n9idk50z0/8AHGsU/RfL6tbf7z8/0pSn/H3X4fS/3zkd+H4+S7PP/Lnof8rFk4/4G9X1fi+o+t+k6VP2 + eX7r1v5uOWSuv4v9ijby+1nOm8P015op+ha/u6+hz+s/3R/46Vf+Ep/uvIb7c/sXb8Wi/wAv+H+GLbh+ + jOPKT/ji8vqX2z/d8/ir/P8A5eDLd9WcGR5U2OxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV + 2KuxV2KuxV2KuxV2Kt4q/wD/2Q== + + + + didongviet.png + image/png + + iVBORw0KGgoAAAANSUhEUgAAAKsAAABoCAYAAACOlAADAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lD + Q1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQ + SoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfA + CAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH + /w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBb + lCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7 + AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKB + NA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl + 7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7i + JIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k + 4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAA + XkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv + 1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRR + IkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQ + crQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXA + CTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPE + NyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJA + caT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgX + aPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZ + D5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2ep + O6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2q + qaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau + 7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6fe + eb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYP + jGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFos + tqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuu + tm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPj + thPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofc + n8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw3 + 3jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5 + QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz + 30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7 + F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgq + TXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+ + xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2 + pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWF + fevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaq + l+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7 + vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRS + j9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtb + Ylu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh + 0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L15 + 8Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89Hc + R/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfy + l5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz + /GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAIH9JREFUeNrs + nXdcFNf6/z9nZrbjwoJYQmyEq7EhKrFGjVEiJKL+bDdqYmLXJNgxwrLL0m8EFU2uKZoYNWrKjdF4c9VY + YjQq1thFA5YoepEqZcvs7JzvH8v6hRUQje33dd6v17zEnbJnn/nMc57znDPnEEopJCT+f4CRTCAhiVVC + QhKrhCRWCQlJrBISklglJLFKSEhilZCQxCohiVVCQhKrhIQkVglJrBISklglJCSxSkhilZCQxCohIYlV + QhKrhIQkVgkJSawSklglJCSxSki4wz1tP9jhcGDSpEltWJblKKXiA33yGYbLzMzM2bNnTx4AoGt/Hyi8 + Gez9rhQ8b4VMJilOEmvdmTZtWtCePXt+t9lsYJgHW7GUlZVh0KBBAEBgtwMqJh+ZGUCbDkC/UKBFC6B5 + s0wIju+RHJcISq2SBCWx1ur9SkpK8LDEWl5e7vwPBVBcDOTnAaUlQHY2sH8/4HA8D5Vaj8AX9Hh3JqDR + TMKCpBWSFCWxVgvLsuA47oGLleM4sCxb+QOAkwFyedUDRRE4nwmcOgEolMtx4fxytG33AZJM8yVJSmJ9 + 9BBSm3sHVEpArQIcDmDzv4Ft299HTs778PJ+BekfbJcMKIn1yYJSp3C1Wuffq1YDfn4/o57HOiQYxkgG + cnvGJRM8TO96j57YyxMoyAf+8Y/RiIymsNslG0qe9QlGpQIEAUhLBf68QrHrqBZ5maWSYZ5iz/qw5qX9 + 30Ybud+CASwLeOmAb78F+ncpASFySapSGPBk46kDvl4HzJ5vk4whhQF18sBWqxWiKIKQ2r2l1WqF1Vop + z+8Qgb/SSUYA1PMCliwGNB67EK9/WRKrRI1CZVkWXbt2hVKphCjWLjyLxYL27ds7D2IIoFQ6BWuxAna+ + ImWlcuZf6xqGsIzzOp983Bfm8uFIS/6XJFaJO3A4HPDw8EDLli27Ll269NDd4lyFQsHs3LlTvC2yVq1a + o0evNlDIuoEKkfj9OLD/gLNnS6MBZJyzp+tuyBXOnrCsC9/BbidP6xgDSax1wGw2F8vqIJA7Bsas/DQT + QCaADQDmAQAi9aNhtazF2nXOblgPj7o1urRaYNO/gbikz5FomiCJVaJaZDLZg2uNpyatA7AOKtVEHD6y + HL/+Cmjr1d7jBTj3yzhg1+7xIGQKKBWkbIDEo2FB0grs/lmFcW8DJSV1i2HVKuDoESDaGC95VolH3YKz + wuEgUCooln3i7MG6m3d1CMCfV6MAREueVeLRwrLAsqUsBg0CSkrv3pegUgPbdwCTZgZKYpV4HB5WhLd3 + a/g9A9j4u9SFHFBYCDT2WSqJVeLxsPLTTIx7Owu2OnRW2e1AeVkfSawSj49lazuh1fO4q2BlHHDmLMDb + JbFKPCbyMksxeBBgsdxFrDIg+yIwaVZLSawSjw9CEiBXOF99qTFuZYH8fMBL4SeJVeLxUWD5EY2fcY5p + rVHQjDNUYBi1JFaJx8dHyUfQ5FnALtzNA+OvDemSxCrxV+FYQK2uPQx4SpHE+sTGrnfZTykAjpHEKvH4 + EEXAbHbGpTXhcACenoCVz5PEKvH4mBoRhOs5gIytXaw+PsC1azmSWCUeHz71Q5BzwzmTS03YbECHQODH + bySxSjxGFFwibBbnKzA1hgoUCHju7mNg/6+1PSV1PEmNKsKgwwtyKFW1N6w0akAQkqRsgMTjwxC3C2fO + AApFzcfwPNCkCZAcnyiJVeLx8Nbklvjm2z6QcbWnrSxmYMRwPI1zu0pifWJCAHoeF/5wvnZdE4IA6HwA + s3XE02giSaxPArEJ+Vi1GvDU1vwuFiFAWRkwKBxP69wBUgPrccLbgeQPLEhKUaKeR+2te54HdN6AzvPV + p9VcklgfFzPe74dL2Tuw+UfnnAB3m4W7vAyYOxcw6bdIYpV4NEyb3QUNdAfx7dfAzdyKN1rvki8tLQN6 + vwRER5Kn2XSSWOvAtWvXCu7rxGFjmqFZi0AoSCBksl7IvDAA+/YBN2445wDw9Kx9vgBCnOMEvH2ALl3G + 3LE2gSRWCRcsy8JisYDn+euhoaF3Pd5sNqNv376Ii4tzekBL2WWsXgFYbIC5HGA551oCnlrnCbVObEEA + qw1gWGDShCVIMq172u+HJNba5EIIRFHEwYMH6zT5sMViga+vb4UQAVz/L1BU7JzPysvrXr7Y+Uo2pcDc + 2V8hIXamdDcksdYJtbpub49QSqGsnCdVyJ0v993LEkaEAOXlgFIBRMxcgYTYSdIdkMT6kN1yxXYvs8GL + IlBSDAS0BIYPi0ZibIpkSEmsDx9aEZPWtf1uNjsnHh7zBqDWdURKwnHJiJJYn5SA2OlJy8udIu3yAtC/ + 3xdP69yrklhriS1d24O+7l0OcPbv2wXntO1yBdCjJ9Cn9z4kGF9Gxl5ekqQk1tswDMOVlpbCarVWXWf1 + AWC326suNFxWBtjKnF2lVAQ4OeDj7Vwdu2dPgGEmITl+BTgWiDdIapTEWpWPP/74ZExMzDoAcgAP9H1n + QojKbrf/cruBNWK4gBsvctBqAZV8J0RcgtWxDWkJ/5Kkdx/2fViLl0lIPPBaUTKBhCRWCQlJrBKSWCUk + /i9mA4YNG9asXbt2+ypa1EIl4ZsdDseveXl5H3766afH3M+bN2/eRLVavdxutyclJSXFAMD06dN76XS6 + nwCUuT1AtwRB2JyZmfnh999/f6WWFrhar9f/s6Sk5O28vDzIZDK0aNHiVmlp6ZuLFi3a7H68Xq//SCaT + vWu32xOSkpKM7vunTJnSqVGjRrsppbvi4+OHuO/v379/w+7duy8qLi4eXVhYCIZh0KxZMzgcjtnJycmL + Xeu7Tpw4sZ2fn99vAIrj4+PbUErN1ZV/xowZ/XQ63X9EUdwXHx//8rRp07o0bNjwF1EU95lMplcYhkHv + 3r19+/btexSAspK93e2gs9ls0SkpKYv1en26TCabQiktquU2qimlh4xG4ysc55TBuHHjnm/atOlvAPgf + fvih7cmTJ6s9f/r06b28vb13uMocFRU1RyaTpTkcjsVJSUmz3Y/v2rWrT2ho6HlK6dmYmJje8vsd6lg5 + QV7XLTo6ehnDMJTjOIqKjsXKW8OGDanRaPyv+3nz58+nAGh4eDh1fWY0Gk8BoDKZ7I7rEELo3/72NxoT + E/NddeWIjIyc3KtXL0oIueNcnU5H9Xo9BcBVPicsLIwCoM888wydPHlyJ/drGgyGXQBohw4dqCiK7r87 + uV27dtX+ZoVCQceOHUsHDBjQmFKKmTNnvubp6UkBUIPBsKsmW86YMYMCoP3796eUUuj1+s8BUH9/fwpA + XvE7xzMMU62NKm/Dhw+nlFIEBwffsU+pVFKWZat8plar6YQJE9pVKssAnU5HAVCj0fh7TWWePn06BUBD + QkIopRQjRoygAKhWq6Xvv//+DPfj58yZMxwArV+/Pp02bVq3+9EcpfS+86xWjUYDjUaDgQMHQqfTAQDK + yspw+PBhHD9+HPHx8Q0BFMbFxXm7TnI9USpVlUkczCzLokmTJggPD799TFFREX777TdcuHABqampwwGs + SUhIeNN10uzZs8M3bNjw6ZUrV9CkSROMGDECHh4eFwD4HDt2zGfbtm1ISkrC1KlT7aIoEqZi5JNKpYJC + oUBeXh6KioqOOhwO4tY5YCaEQKPRVPnBUVFRUZ988klUcXExWrZsiaFDh0Iul2cBUGVnZ/tt2rQJq1ev + xjvvvHMdAFm8ePFPubm5WL9+Pc6dO9fX4XDc0QkRGhraODs7G4QQdO3a9csK52EBAA/nMpmuPLDg4eEB + uVyOwYMHw9vb+44bYrFY4OXltRoAwsLC9nXv3r2nawTY/v37ceLECfj7+yMsLAwMw0AQBNSrV680Pj7+ + 4ooVKwAA6enp2/Lz87F27VqcPXs2qLoyBwYG6nied3nMrwFAqVRCqVSC53kcPHgwnRDyMaX0dm+cKIp2 + jUYDDw8PiKJ4/ysj3qdnXaRSqehzzz1325O4tn79+jV87733KMuy1Nvbm86dO/f1Sl6UAqAjR46s7FkP + AqAvvvgidf+eCRMmtBs0aBAFQAMCAui4cePaUErB8zyGDBlCAdCgoCA6Z86coZXPs9vtMBqN15VKJeU4 + jkZHRy9y7Rs6dChVKBTUy8uLchxHDQbDVjfP+m9CCO3Ro8dtz9qrVy/fTp06UQC0T58+Lu/AVd6MRuNh + uVxONRoNjYyMnFhhp3SO46ivry+dOnVql2q8+A6GYWhgYOBtO0ZHR38EgAYGBt6uFSIjI8dqNBraqFGj + 2za4ly02NtYGgI4YMYLW4d4my+Vy6uvrSyMiInq574+JifmO4zjaunVrOnjw4CaUUrzxxhtUqVRSb29v + SgihsbGxVb5n1qxZgzUaDW3evDmdMmVK8P161r/UwKKUonnz5lXmtd+xY0fuRx99pOrcuTMKCwuhVCrX + Vz6+8r+VEUURvr6+9Sp/tmLFitPNmjXr7efnh6ysLDzzzDORAGAwGCJ+/vlnaLVahIaGRqelpW2oEohz + HOLi4p4ZPHgwBEHAiRMnZrmXwcvLC0qlEitXrhwwc+bMkBriQABA7969E3///XfUr18fnTp1GrJs2bIj + 7sfGxcV179OnDxo2bAiZTOZfYYuEwMBA5Ofno0GDBl+7/96zZ8/2E0URoaGh2Lp16426NIQ9PDx87/U+ + ORwOecW/d12G/rPPPkvp2LEj8vLyoNPpvnO7Di5cuDBcEASEh4dj48aNVyvb1cPDAxqNBkuXLsW8efMm + 1mDT+9bcQ8kGUEqtPXr0AACcOHECgnD/nn/p0qV7g4KCXFXd2wCgUChGm81mPPfcc4iPj69xzGdAQMCX + LMvi3LlzCA0Nbez63G6346233uJfeOEFXLt2DdeuXfvZbq95mZ7i4uLJlFJ069YNgwYN+qmm47Zv3y7L + zs4mSUlJ0QBw8ODBgvDwcFBKkZGR0aLyjZoxY0bP3bt3u6rGUXV1DhqNptPIkSNbjB8/vo1rmzRpUuCQ + IUOaPIh7l5eXVxoaGmoFgD179jSsXOaZM2f23LlzJ9RqNURRHFP5PJvNhrFjxyI8PBxFRUXIyMhYTgh5 + oC+NcQ9L5FqtNhPA8zk5OXj55Zd99+zZk3e/1woICAAAXL58GQDA83w3APDz86t1MIrD4cjUaDQoKipC + UFDQSABLKKUQRRGiKB4ODg7+8tChQ8t/+OEHJCYmHoiLi+vuKo+r6iGEIDs7GwDQuXNn8aWXXqocc/p2 + 7NhxNACRUipGRkZaoqKi6uXm5m754osvzgJASUnJIE9Pzx/379+PuXPnjgTwNQB4e3t/X1BQgMGDByM5 + Ofnru9mb4zgIgoC1a9cuYll2UWUPKYoiWJbF3Llzh7nXMjXVZLVRUlLyure398Zjx45h7ty5rwNYV1Hm + 9QUFBRgwYADS0tK+Tk1NrVIDMQyT5evrO7VBgwY79u7di9jYWAsA9nGLtS7wrqpDrVb/pe+RyZxzlbo8 + tKVinSilUgmmlldGKKVlLMvC4XCAEOLlVh09u2DBghWU0uVpaWn4/PPPu928ebNL/fr173iT1fW9lNIq + +7p06bIqISEhxPXAyOVyWCwWREZGpqFi2PXChQs3X758GT/88APUavVnAL4WBAE7duxoWNFg2en6fXWs + 0u8Y3uj67EGxaNGiTdevX8c333wDjUazCsA6nuexe/fuJhW/e+/WrVvFaqr45kuWLNnp5eWVFR8fH5Ce + ns6Yzea3W7VqVfREiLWmGEQQhEAA0Ol02LZtW8HdnvCa9lFKcfWqMzRyvYzn5eVlBqAuLCystWwsyza2 + WCzQ6XSwWCz7q/Poe/bsqR8cHJx/5MgR2Gy2g4SQr1wPgMuz1qvnDKULCgp83YRzJCgoKMT10OTk5ODq + 1auoHFIQQtC2bduvN23a9PqWLVvqxcXFISYmJuL48ePw9/fHmTNnJtRVpEqlEmPHjjUVFRX9JJPJFJW+ + g7NYLEVpaWkn6xJK1IWAgIAvGYZ5e9OmTZzJZEJMTMzkQ4cOoVmzZjh58uSY2q4ZHx/fOiwszL5lyxYc + PXp0JaV0lPwBvEb+0HqwTpw4AQBo06YNKqcx7rmADKM+edJ5D/z8/DIqYs5VhBBkZWVh/PjxbWp8Ejlu + stVqRePGjbFo0aLt1R1z8ODBgv79+0fXq1cPGzduxN69e99wS62hffv2uRXCvl3lAUBISIjx+PHjioyM + DMX+/ftlr7322u0quTIHDhyYGRAQgNOnT2P+/PkzlErlUrPZjOHDh6O2Dg93QRBCUF5evuuTTz458uGH + H+5zbUuXLv11+fLlJx/k/Tt9+rSxZcuWyMzMRFRU1PtqtXqx1WrFkCFDqjSsqtMTpVRo27btqIYNGyIj + IwMnT55c7+p4eBxiZVxew2w2F1fTS5S+d+9eKBQK6HS6z+ooSty4caPU/XOj0ZiVlZUFb29v3Lp1ywQA + f/zxR2qrVq3w559/Qi6Xn6nuerNmzQpbtWqVLwCEh4eLtYULKSkpKZMnT8atW7dw7NixO7IWJSUlE3Q6 + Hc6dO4fY2NhbrvNeeuklUErFig1ms/l2DOmeIRk4cCAsFguuXLmSvmHDBqjVahBC3rtXewuCcM8Ltt5P + iLBx48argwYNgtVqxdWrV//x448/qjUaDVQq1ey6nJ+amvr1lClTzvI8j/3798OVm30sYQDDMOB5Hs8+ + ++zMyMjI/YQQpUwmayqTySK/+uorZUlJCcLCwpCQkDClDqEEysvLYTAYIh0ORx4ATiaTPW82m+esXr0a + drsdY8aMQXp6+jYA+Pbbby8ZjcYtycnJYStXroTZbKYNGjQYcejQoV/lcjnTo0ePz7du3fralStX0K5d + O5w6dcr/bmVYtWqVNjg4uOTUqVN3xMGLFy/+SaVSISUlBampqeqbN2/aNRrNqIMHD+6Uy+VMcHDw6/n5 + +en/+c9/qjQ43EKScVqtduVPP/0EnufRs2dPJCQk/PMewi04HA74+PhMi4yMDCCEKN3uh8JqtV5IT0/f + XtdrDhkypElQUFAmAObo0aMBmzdvznFLr43y9PRcv3nzZthsNnTv3h2JiYmL63p9vV7f9ujRo3T79u3u + HUGPTqyEEBXP88jLy8OiRYumEUKmUUohCMLtxki3bt0QEBDQu/KNs1Ws9mypupCuB6UUFy5cQGZm5gJC + CFyJf5eHGjZsGHbv3u3tFhe9yjCMLTU1Vb5mzRp4enp+5+vrC5vNhl27doFSilatWiEsLGzKggULble1 + VuvtOXg93FM2er0+6fTp0/ry8nJYLJYqHiklJUU2depU+xdffIGPP/4YWq12ff369UEpxYEDB2A2m8Fx + HAYOHAiHw/GKu80SExO/PHr06Mpdu3YBAF588cVD1TWsCCEqAC4vzVQIUWWz2SCKIhYuXDgWwFh3b2m1 + WtGuXTvY7Xbift0a7I62bdtOiouLUwPA/PnzJwKIq7w/LS3t25CQkPXbt2935ZuPV5d9cdnUbrdXecrl + cjkCAgJeycjI+LmwsBAKhQIcx8keqVh5nj8YHBw80RVHuQynVCrh6+uL1q1bZ23durXb0qVLq7SeVSpV + Rvv27bu58qYVDbHtQUFBz6tUqtvXIYRArVajcePGaNGixRaDwfBqdTfWZDIpbDabITs7O/7w4cPIy8sD + x3Ho3LkzQkJCkJWV5b9gwYJLbvEnrl+/DlEU78iXJiUlxXAcN3nTpk2+HTt2rBJ7UkoFAMTb23tpdnZ2 + xLFjx5Cfnw9CCJo0aYLAwEC0atXqK6PR+GZ1ZZXL5ejZs+f2/Pz8EB8fH1y+fPnNGhpSpzt06IDWrVvj + zJkzfIXIznbp0sWV1ai2WrfZbGjdunW1Xl2tVu9t3759r6CgoCq1hsViOdyzZ0+X4I5UEz6IBoNhQ25u + 7lAfHx/k5ORUW+a2bduaz58/r2ZZ9lf3fenp6ds9PT2Pbdy4sVPz5s1x/fr1a/fdmL/flAchhHMXu8Ph + sDJ3mX2EEKJ2H4FUkTyufKJ4r42yKVOmdNJqtQEOh8O2ePHi7TWNcqpoAClpLdOcE0LUgiCYa8vhjh8/ + vo23t3cbAGJBQcHZlStXZtbRbmq73W6urcFBCJE7HA6+si15nodCoVDWFpnVVmZfX996eXl5pTVkc5iK + h/GvlFldk83ratOHJlYJiUeNNPha4ukQ67hx456vLkUSFRX1fkRERE9CiLJ///4NQ0NDGzscjhrTKa5G + 2ZtvvhlQ0RVapdoGnEPTqu3/FqmrngTeeqtlRZ2jRpTBUGUZdFG8c4pJ1/8NcWvx5kRnn27vV30R2EtX + W+qHEMK9VfFd7vvc01YVI8t26/X69CrHO0Rg5NgWtw+cp5+Id6Z3u6N8ruNnzh0AY/yeutwTV0eNq6Ea + FRWlr3xMYGCgrnfv3r4AEBER0VOv1y+7wyYuomL1CB3WuNrpOSe9E1hl9e7AQB269ve5/XdM3Kdwu+/4 + CxX5fYUBPM8jKSnpDCHEXxCE1ZTScpPJNDs+Pn5NYmLipHHjxlkaNGjwjsViyaSU7tLpdFkxMTF/0+v1 + k2UymX9SUtJ8g8GwdtOmTe8NGjRoAcuy3QRB2Mxx3FQAZZTSixaLpU9RUVF7X1/f1yilJQqFIgVAmdls + np2amvotDHFrsG71exg5eiIoeCjkUSCkIez2D7Htp2i0al+OdV9qYEy4CIvFCJbxQV7+Zlz+Iw89+qxC + vCEUMabPYYyegLHjKeppO8BL9zw0qrUAZVBU9gqWfLATAObNmzeWUmotLS29qNPp+gqCcKO4uHjN8uXL + icFgWGswGMYkJiZuTUhIGBgREdFdoVDsSU5OJvHx8fT8+fP+/v7+F2Uy2bqSkpIVHh4e/ZKSkmIQm3gY + FMEgJBcx7zdCYsqfoPQ0isqSoFWPQJJpJvSxC1BWtgVLFv4CfWw8ruUYsPpzBWLi1yPR+CYMcRuREDsE + FbGiwWBYwzBMGIBLPM+vczgc5TzPX7LZbD8vW7aMEEIwa9as1zw9PTdQSm2FhYVhWq12DMMw0xISEpwt + M0PC90gwDIMhYRUO75uPzl2vA0gBbzsLtXoRbhS9Al/PoWDZbiBsN0A4B31UV4x6vRnaBF4Gw+WBt/4T + u37+CD6N8nH4vBZTRi1FkmkSYkyfo6TkSyxZ+Msj86xGozECgJ/JZFo9duxYCIIwKzEx8XBRUdEbU6ZM + aVdeXg6lUpm+ZMkSwcvL6xeLxZIgk8kgl8u7sywbXJGO6de0aVOwLPsSpbSI47iomJiYAj8/PxshJKCk + pAR+fn6nZDLZAKVSmR4YGPiRIAhfsizrHD3FsG/gjfHZMFvTwDBL0arlDZhijoPjZiAkLBIXLwHRsWmg + tCFu3vwUCmU8WFYOlTeHP7IGIDbxdwjCeOiNI3H9BtCo4ftQqz7D34dvQ5cuW6DV7KiUI20iiuI3jRo1 + OiyTyfqo1eo5ubm5iIiI6MkwTLBMJoMgCAP8/f01giDYDxw4AJPJtPTIkSNo3rx5r//+979gWXaoQqFo + wbJspwr3HIw3R28HdZyEIS4C1280QUFBGLSqcHBcn4ov7gSWcabYONn/Q34+8H7MBDCkFwYOBChC0G9U + PVcDimXZ0RMnTtwuimIWx3GdZDJZUw8Pj9HZ2dmYN2/e6wCg1Wr/abVaZ7777rthQ4cOhSiK07Kzs/93 + eGZpyVDEJpxDTs5YdHzhdVzIAjhuLDiZH/Yd8EUj73Rw3HsgpCVeH34IFKUABf7W5lVkZQOh/TIgV0Qg + JGwBSsqACSPGg2W6VSSD+0Ehb/ZIwwCr1ZopCILnhg0bsGbNGrVKpRLMZvNiHx+fokaNGu1r3bq1mJub + K09JSQEAwdXyJoQwDMP0mThxYjsAQocOHWYRQpqWlJQkEULEQ4cOIScnBw6HY3ujRo0yKaXbjh492lcU + xcPHjh3z4jhusEKhmOq82bCCt34Ar3rHIJMV4PwfwJ7fKgrImzAghIe5fBoIU4Rnm67A9z9w4O1mqCCH + jw9QUhwNhfIWFMpv8HJfEUW3RgME2LkHyDjIOBhy6bmRIz1dCXeNRrPNbrcvIYR0EQQhqFOnTqK3t/dm + ALqoqKgZlFJcvHixXC6X1+vatSsuX74c0bVrV6hUqrTmzZubzWazUqVSmSilt26HLb/uBQgaQ6GIhJcn + oFACMvkcUFruynhB4zEakfqRyC9ohxdeABTKFBBSD+2C5sBiwScqgeN5Hjk5OaUVXbswm82vMwwTxLLs + SwDe7tGjh1mj0aysCA2y5HL5a4sXL+79yy+/fCCKIpo1a4Zp06Y5x65q6wHlFj2aPmuFXBGL1q2AgkI/ + KOQmdO3yIU6d6gNCPAF44exZIPtSP0yYEADefh1aLXD4GECggdU6Hv37inAI6QDRYL4xEharH2zmK49U + rOnp6dtFUTSeOHEipLy8/F+FhYUvp6amristLR1ns9kSTSYTy3Hcizdu3EB+fr4xMzNzHwDk5OR8IIri + QV9f32E3btwYyHHcGzabLTk9PX3btWvXOnzzzTcBoihmmEymceXl5YaEhIQhQUFBCfv37x/GMExjSmlB + Tk6Oc8GyW2XDkRCbirz8d3HuwgsQhAPY+YsWt8peASFDYNIrIJO/BZ5PRZJxGga+ZsTRA7n47rscqLR/ + x+K0LTBb3oNA34EphoVKPgqF+a/hZi4AwqVevPDK6PbtVzgcDuTm5n576dKl95KTk+fyPL+Q5/m/l5WV + tS8tLR1nsVjmKxSKSJvNNoRSKmRnZ591OByhfn5+bxUUFLzI8/wSk8mkuXDhQlNBEFbn5eUtdAa3mI6r + OX7g7V/hVukEbPvNGzv2e8NuX4ibRTOdXq70AzBsSyiV4WBlL8GkJ7DxepSUjoRcGQFCR13t2eldvV4/ + Ui6Xo6ioqP+ZM2daKpXKj3NyckbxPL+2tLT0ZZPJpLHZbNGUUmzcuHEEALVMJuvO8/z6S5cu+cfHxxOL + xbLamXw1/x1pyRtQVDQQvG0xTDEEVmt72OlyfLZMj2ZNX0Fx0aswl03FyTNN0LxZKlauzMLiD7agYYOf + kF8AFBcPAWH/DlMMC0pGoLTsXcjl0yCTpWBJ+i/3HbTe7ysGD2MbPHhwE7vd/kSUxS6K6Ozv71ndPgDy + +3m95GFsL7Zvr3tSbPawNynPKiHlWSUkJLFKPLX8zwA1QLelQxOYAQAAAABJRU5ErkJggg== + + + + logo-didongviet2.png + image/png + + iVBORw0KGgoAAAANSUhEUgAAAWgAAABcCAYAAABDeS/aAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lD + Q1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQ + SoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfA + CAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH + /w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBb + lCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7 + AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKB + NA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl + 7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7i + JIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k + 4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAA + XkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv + 1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRR + IkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQ + crQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXA + CTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPE + NyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJA + caT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgX + aPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZ + D5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2ep + O6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2q + qaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau + 7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6fe + eb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYP + jGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFos + tqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuu + tm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPj + thPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofc + n8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw3 + 3jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5 + QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz + 30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7 + F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgq + TXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+ + xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2 + pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWF + fevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaq + l+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7 + vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRS + j9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtb + Ylu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh + 0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L15 + 8Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89Hc + R/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfy + l5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz + /GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAPo5JREFUeNrs + nXecXFd593/nnFvmTt/d2d6rtOoryaqWJRfcbTA2IZCADQn1TUJeCC8lbwIEkhAICc048AYHMNgUy9iy + jY2xXIQtS1Zvq7Kr1WrbbJnZnT63nvP+MTNryViywQJL4n4/n/lod3Xnzr3nzvnd5z7nKUQIARcXFxeX + 8w/qDoGLi4uLK9AuLi4uLq5Au7i4uLgC7eLi4uLiCrSLi4uLK9AuLi4uLq5Au7i4uLi4Au3i4uJygSG5 + Q3B+8/73v/9iOA1mGMY8QkgQgAPgnGVHUUqpZVkYGB2NRrgY/IHBSAisEcSj4MCWHDradSxYJNDS7EDz + GvjOt0w0twpctgEYPAH4fIDqAXJ5oCICyAxIzAD7jgA79wD5iQt64P++owM/LivD+gUL0NHRgXg8Dl3X + ceedd7qTyxVolz92CCHgnNcNDQ19PJPJtDDG9HO5f8dxCCFEqWluPl4J8gE6lQJs5zOIjdahvDwDzWuD + UgJZnoIs7wIhDwOIu1fGxRVol3MicBf68RNCKoeGhjaMj483yrJ8zs6JEALDMCBJEpo6Ozs8hFJi6oBh + XIuxgTqoHmBoGJiYBHbvBlTPOIh8C6pqklDkJ8HYAxAi437LXFyBdvljFmhOCBGU0tLv52zfpX2apgmT + UAjLAiwLcDiQyQDZbOF32wYIqUF5xc3YfwDo61uH6qqbsGjRM+D8QQgx6n7bXFyBdvmjhFIKxhgYY+dU + oEv7pJSCEgowCjAGMAlgApAkwOMpvkMAeh4YOA7YdgsqIy2YmLwC7e03oLnl22DsUQjY7tVycQXaxeUP + wcurNSoKoKoFsU6ngX17yzE+cR1Gx7qwZEkPmpt+hOR0nztwLueFYeMOwflveV7obo7zTrA5B7gANC9Q + XlGI2ti+vR2PP/5J9B+/Cz7fle5AubgC7fKqZDIZGIbhDsRrhRRfv41Ye71AKAicHFTx6CNXYmDgK1DV + y2G73g4XV6BdzkIulwOlFIqigHPuDsiZdHl28fF3eNoQAqAUKCsH4jFg85OLkc/cidaGq92RdXkjcX3Q + 5/sdlFJomoZQKATbtmHbNii9MO+rv233nldz7ZREuRTNQV+PK0gIAAIIhgrRH0cPdWPunP9EaNVfwbae + huNcmF8gWcIFe+wurkBfCHDO4fF4EA6HkUgkLmiRfq3Ytg3Lss4q1KU4aFmWYRgGDEJf8i9DAIQULGMh + fnOx8MyDDfj8QCIJHD02D7f+yRfR3v5uzEwfxYW2FiBJwO4XgRP97iRyBdrl94njOFBVFeFwGMlkEpZl + XbQiLYSAoijw+/2veo62bcPj8SAQCMAnig4OLgAjD9gOQFlBpFUVkOXXJtaCA4EAMD0NbHpgBW665TMI + Bd8NLuxCBMiF8qXhxXN1F5ldgXb5vUIImRXpUCh00VrSnHMIIdDa2jrW3Nx8QNd140wKQwhBPp9HPp/3 + tLa2jEY4HBpLEuj6Nlhd8yB4AILXIJtlmJ4G8vmCUCvKKW6Ns7g8/AFgMgZs/tWVeOtb3wvT+A76j10o + dzkMhEKImgYk6i41uQLt8gcTsJIlnUgk4DjORRWGV/JTh0KhJ8Lh8Aei0ah5phuRJEnIZrM4evQo1m7Y + AKEoQF0twMX7sGxFE3zafAjnFhw+XI6jx8qRSMxBPOZBLltIXqHs7CJNacHqnhivwvHj70ek/CE8+LML + pnrSV9vbsaWqCiHG3IlzgeLeWi9wkWYX4eQjhMCyLDDGEA6HIUnSa49g4Rxw+DRsay9M60ewzNtgWVdg + 7do/xzve8VMsW3YCZRU6LKeQAn62m5sQBSHP68Du3YuQSv/9hTSOKqWQCDl3pQNdXAva5bWLdMlPm0wm + L7pkFlqA+Xw+MMYQj8dfn0vHcQ7C0G/HqlWLEAx8HHv33opYXIMQBZfH2SxpVQUSSRl7912GhsYyQMyc + n18KFJxBslyw/n0+N4LDFWiXN9olUHILXIwZh6UIlvLyckxPT79+lw6h+3Fw37vQ1noCnXM+jRe2Mpjm + mUVaCEBWgEwayHg7cPt7PwIuPgtx/tmlMUZhEILE9DSEbSE50A9pxq2s6gq0yxttbYIxBucitZY459A0 + DRUVFYjH469TpIvhd/H4P2LlKo7Y5Gdw+FihuBI9wz4JCgWYcjkf+gfegmDFeSjQBD+OeDEoU/z0kQcx + PDSEzq4uVEYiyOfz7iS5UOe2OwQXvgWtKApCodBvnQhyIVrSFRUVYIy9vnOVZODgPuC73/4sFOUL6OoE + spkzR6OV3CC5PLBrdwiMXgJZAiR2frwYQ5IRmMWnqdmxEa732bWgXc4P+6lYevNiTgcvLY6WLOnfWqQp + LQjaqZay5vkCGhobMRG9HdlcYVHwlfbLGGAYQC7bjMrw34KQd50vg52EwA/gYEJweLhwo55dgXY5X63o + mZmZi/5cS5Z0IpH47URaNwDLBpxT3pPNG1g9ZzumJt+GrS94X6od/creEZgmQTrVAybR80GgbQD/ozKM + KhJUTi7qpyhXoF0uaAv6lUqTcs4vusXDkrsjEokgFou9FlkvFPLfthcYGC74oEOthX9PTgPf//ldCHu6 + 0NDwt4jFzmxFSwywTaDvOEGkjkIQ4I0MYhOAoEC+ygciJLzBR+PiCrTL2SxLSil8Pt9sASHOObLZ7EVp + VZXcHZFIpHADOlsrLcaK8c6koGAeFSCe0o4Kcc7dSxLIxIHhk3hFK1qIwn5sBxgeUVFe2whKjr9RiigA + cEJgEgEGN5nbFWiX816gT7WkgUKmXXl5+Wxa+MUYhldqd8U5fwWPgygoWWtbQVwvWwGsWfqbySlCAOVl + z2Dr83sA2gPOXzmBhVLAMoFUQkFQqQFjx/EGeTmylOIHPhXTDkeeAorr2nAF2uXCEmsAkGUZwWAQiUTi + onN32LaNcDiMq666CtPxOEAIOBcvWcuEAGm7UDxJWAU3B3mFYkcEQNraBerZiWCwB7ZdSPb4je1IIRnE + MCkY1SAxgP/hxzNLCH7g03CSAjYsyKJgRbsS7Qq0ywXIxdo2SwgBSZIQCoWQsywUipOe8sAvRKGim+UU + xNU+i7UriQyCwUGEw8D4+JkTV0hRqAtl8f7gspgSAt8CR4J5oQgBJsQbcBQurkC7nDMRI4QgEAggnU5f + lMWVbNuGTAjSAL5KOf4WFH4hgL4+IJcFyGsJ9ycAIxyatyDqr/Kxb5Qm2gCiQsAnBGjxdxdXoF0u9Ast + SZBlGZRSOI5z0S0cEgAWAU4IATOnA14HyGYLAv1aC0qpasHPTF5NmgUp+Ev+sMtzSQh8l+sApe7EdQXa + 5WKzpEvuDr/fj3Q6fVEtHNoAghz4YMZEuV1c5OtoB1LJQsGgVztPSoFMtpCMcqZthSgkuWiaA1ukITj+ + kIuEhuA4QQuWsxu14Qq0y0WKoigoKyvDzMzMRSPSDgr+2BbbAVC0ggN+wLZevawoUFgUjMUrkEkXWkW9 + 0gMG54Ua0l6fiYmJSRDyh0mn5gIJieHeqjAYIW59BlegXS5mSqVKT+1xeCGLtCRJCIZDCDscNB0tRm2g + YDkHfIBTtH4lqWAp/4Z/hAASa8Pho52YSQCaB7+h0IQAtl2IkW5uspDLjf/exJmQl9wyQmBGYvhe0Ide + KqDCTeV2Bdrlj06kL8Qeh5RSGIaBeDyO7poaeEUxxK4knIQAiqfQtkpSgO1bgejYb4bQGQaw4fKr4fVc + C0sH/L5Xtowdp/DeeXM5LMf6vbg3JAkHpibxX4cPQWUMxHFgBYPIt9RAE6LYENfFFWgXV6TPc3HmnCMe + j0PXdXDuFNbuXo7gQDoNeLzAycFCpuArEQjUgikqmHI20xZQVRtlZc+BMX5OBbpooU9TgvuyM7g/PQNV + kmDZNiolYEMxhp27X1tXoF3++EQ6FApdMN3CS5Eo09PTyOVykCQJwFlaO1Fa8FisWQvMzANyOuDYxcQT + DlRGlmF07Ar0Hil0ITmT9awqgBCD+PG9/w6cS60kQD6LsfZ2/GLDBpzkDiKUgVEGmwoEqNtT0BVolz9q + kVZVFcFgEJlMBpZlXTDi/Np856IgxPMXFirZxeKArr8k3uC3YuDEpTBNQNMKVvfLrVvDACrKgSWLsnh0 + 07Fz7X8eCPixcX43kgTwCTfxxMUVaJdXEOmZmRkYhgGPx3PexUqfKs7ZbPa3y5AkKIiy5QAz04BXA0JB + gIvbsGXLO3H0WCHqQ5zBMHYcQFbzmDvvcWQyOGO9jteKKAo/pRjMZXB3WRDDHZ3otm1MuF9HF1egXX5D + M4RAPp+HZVnwnK0u8oUmzq9kDZcFgebG9Xhh29+jv695NsJDiFfeXtOAOXP6QNiXcdnlry+8jgv0SwyH + bAOyYWCvZeJgKoHGfB6ivNz9Irq4Au1yJv0i0DQNiqJA1/XzIvyOUgrbtmfdGq/PR14UVp/vShw6/FU8 + uXkBshkgGDpz0olpAFVVeczv/gW4FUfG/F3vgAAHTsoMjzMVO7JJ5BIJqIQiZDuuW8PFFWiXV7eiPR4P + FEVBLpcDY2/sAtW5FWcAsqzA57sahw5/Ebt2zsf0NBAMvrI4U1pIF/f5gZWrD4Dgi3Ds32VQAQDHZAlx + mWKPLOEkIygXBIwUngQsV55dXIF2eU1P4JxDlmUoivKGJLEUfd+EMUYMwzg34izJFD7/zejruxS9vW/D + kUNNMM2C5fxKfmdCChmIjgPMmRvDJct/gMlo8ncx2I/KElKU4gVFRr/EEOAcfi6QcDNOXFyBdvldBFLT + NBiGgXQ6/QcX6KLVnEqn07lUKoVsNvtbWPKkUC9DUYPQPBHIUh08nhZEx1oxMfY+7N/XiJODgNdbsIzP + JM5CFKznqhoHixffg0zmTlDptxlEAMAJieBhj4rhojCXc+6WB3VxBdrl9Ys05/wPHslRuhlkMpk58Xj8 + 7QCox+MhZ9uWUioxxtJMiIcATmDb78VA3wKANwK8G7F4O/r6ZEyMA4oKhMtOE9HfEHhBgEwa8PuB1as2 + IRL5CgwTr1mgBdAvU+QJ8CuZYpICZa4wu7gC7XKuXR2cczDGIISAEAKyLIMxBtM0fy+fSUihM3UikbiK + UnrVq21vWRZkWUZ8enqMCvxCJGaAvP5ZHNhXh0wW4DYAWojCCIVx1gJHpSeFdKYg5Os3bMGSxf+GdGb0 + tYTUCQDHKWBQikcViigB/ALwunHNLq5Au/y+RJoQAsYYGGNIJBJIp9Ooqan5vVjXpZ6KMzMzmJh49Yhg + y7KgKAqqm5pgEgo+kwAMEzCtQmEjRTndWj6bOHNRqB+tqsCVl2/FyhWfQzqz/dVU2QFwkgrkCcFGCUjJ + FF4AQfGScLu4uALt8nsRaADweDzwer04evQoent7cdtttyGfz//e3CuyLEN+pb6ALxNz0zTBGIPH44GH + UECVC5IoScWuVOLV7giFf02zEE7n93OsX/8Ull/yeeTzW84mzDaAqFTo5HIf4chQAg2FTEAXF1egXd4Q + wVYUBX6/H/l8ftYlcd5ROiRyBhOWkJdKiOZ1QKZAXX0GK1dtxPzuLyOfP/SKdT0FYAGYYgQzBPiZymAQ + Apj8pWp6Li6uQLu8YdonBCilYIzBsqxZH/V5I8xCFFuOkNPF+VTxtKyC1awoQDDooHtuAvMW/Cc075dh + GOZpdaOL+7AIkGAEkwT4icpgFyeQIgSs4mauPLu4Au1yXuA4DrLZLHw+HwghrxqOV9rmXIXtlfZz2n5L + r5JU0qJvmfOCcNt24WdJAvwBIOCPY92lz6Ky8suYSmyDx/uS1U0JQBlMOMgygmFK8DOFzhrmkivILq5A + u5zvLg/LsuD1esE5L5b+fIliDQ1mmibJ5XKQZfmcCrRhGFAUBZZlwSK0UDfDMABTLwizxV5ydUhyIZqD + sQxqaqdx5ZVD2PLUFzA48EvU1KBQQbQYciFRZG0bubyOEVXB/Qo9rUWsK8wurkC7XBDuDlmWUV5ejlgs + hqmpKTDGTrNuOee2LMumz+eDJEn2uUx8kWWZeDwexgXPcAEBSSLgHPBpgM0BSgWYZMHvz6GsLIWeHoba + 2h/h4MGvwrKiAAgEL4gyxymtsRgeGerHPekkVi5dCuoUamW4/QBdXIF2uSBF2nEc/OpXv0IikZgV6KJV + 3X/HHXf8sLa2ttU0TQPnNvJMIoQQg/MtIRCTdS/wQogRTE15oHry8Gn7ANIPS+zG/r2Pg/NYsSmsA8cp + 5pUT6JRAlyiYJEGxLTw0dBz/duwIGurqwF1hdnEF2uVCxrIslJeX49Zbb8X3vve904r+O46TVRTlXzwe + D3kl6/nVIkFK7ylt8/LfAcA2TZsRCihKHpxfCVmh0DQBj2pDEAeEF9bxOAc4hy4x2JSCqio0xvA4JXi2 + 3I+WmjCeefoZPPfidnR0z4VEiOvOcHEF2uXCt6IppfB6vbMCqqoqhBCQJAmcc9O27dm4aiHEaUJ76u8v + p5RyfmpGI4DfqAntOA5yliUUQjK2EDCyWfjVQubj7CJhYUNsbKjFQa+K8isvRzqVxtRMDEoggBwlSDs2 + TNuGRFy72cUVaJeLgJIVzDmHbduQZRnvfve7EQwGYds2AoEATNM8zQouZSkmk0moqgqfzwfHcU6zkhlj + GB0dRSqVQkdHB+LxONLpNLxeL6qqquA4TkG4bRujiQT+hVLUVlfDCnnxw3vuwd1XXoVVTc3gADiTIBGC + ey0dzxKOIKVIezxI6DrMfA4+AJJw3RkurkC7XOTWNCEEoVAI4XB41t1h2/Zs9/CysjLoug5d1+E4DlRV + /Q33RUmsbdue3YfjOLBtG47jgBACx3EwNjaGZCKBUDgMU5ahAhCShIl8Htf96IfwaRoeuOk2jHR34kXb + gG4ZkASgAJA4hyyEK8ou5yXu9/ICEDsAhFLKiq/TIGcJiSjWtWAvjz1+pf2UdvdaWkkV90sJIQwAI4SU + XqdtVxLWYk1pRiklnHM4joNkMgld12ddHqecDxRFwcjICAYGBiBJ0mx9juIm7NSxKKV764YBQghkQqAS + wjyUUgIgYdsYTadx8wM/xk/HR5HzesEByIXzoIS85McghMwew8vGnp5lzCilhbbbpXGmlDLGzrZ54f9f + YVxL15mUXDmnun9OvY7F60pOvaav9F045f30TNu4uBa0y+/uOmgH8FbO+Z9xzjNCCAeFMFxKCJkSQvwK + wIOEkLGXvVUWQrybc/4hIcR9QohvADABLOSc/yvnvJpzngVmw3tNQsgBzvlPAGwnhDhnEmcA8zjnn+Kc + rxBCCM65xDk/BuCfAGwruTuKLyqE+GvO+R2c88cJIV+ilM6ULODitmHO+ec458uEEP8ghHjaMIzZllvF + bS7hnH+Uc76Mc+44jqM4jjMhhPh3SukDpxTzX8g5/5oQggF4D4ABAJjQ8zANAxoAvZCSXSeE+LwQogfA + xwFsFkL8I+f8FiHEFwHcVzzXq4vH5gghXt5ORQBQCCESgO8IIX4ohHiX4zh/URzT/KmCWBR8AcDLOU8D + +DIh5PHS0Aoh/sJxnE8KITYSQj5JCBGnnP+pTyeXc87/TQhxCMD/BrCBc/4pAC8QQr4AIHbq9Sq+/x85 + 5zcJIf4BwDMAcu7scgXa5XVSXV397t7e3pt/8pOfLFYUZfaxvzhZBYDL582b93dvetObHnAc5++K1hQk + SapJJBKfvf/++xvmz59f5ff7725ubjYlSfrY/ffff108HqeMsVkLtri/NT6f77b169fvlCTpXwBsPzVS + wnGcysbGxg+fPHnyL++6665qx3Fkx3FKn9fc0tKydNWqVXsYY2/lnOf7+/vh8/lky7Let3fv3vmSJHWt + XLlyWzAYfMiyLAQCAaiqCkVRbt28efPbJyYmquvr65c5jvO0ZVlobGwEpbQNwMf279//lmeffbbacRxW + +kzGWEskEvnu0qVL38cY+3O/3x/nnLfcf//9y2ZmZoLXXHPNv4bD4b+2bXuSEIJ4PI7e3l4AQFNT08ee + eeaZ92YyGaxdu3ZOU1PT2NatW2964IEHFuXz+fVVVVX3rV27Nnzs2LFrvvWtb62UJOm0yJQSnHN4PB6s + XLkykk6nn9R1/b0nTpxYmc/nZzvAlBZLLcs6bZFz0aJFB+bMmfNLzrkIh8P+EydOLPjpT3/a3tTU9MEb + brghblnWlxhjSKVS2LlzJxzHgd/vr2ltbb1z06ZN3V1dXR1VVVWfdxznoy+88MIKy7IW3XTTTROyLH8D + QKa9vR1z5syB4zhtO3bsWPvoo48uXbFixbW1tbXTALa5s8sVaJfXicfjqdB13R+PxzF37lzMmzevFBWB + qakpcvDgwdCzzz4byufzf3PDDTfIAD4ihICqqhKlNDI1NQXDMAKaplFJkiBJUlUsFqOZTAbr1q1DRUUF + HMeBYRg4duyYOjAwULdx48abb7rpJl9TU9MnZmZmdgGAoija8PDwB7Zu3fqpsbExtba2FitXrjQ0TUvm + 8/nQ/v371Z07d1ZPTExce/PNNz8YDoff6vF4stFolBiG4Z2cnEQ2m/W2t7d/s76+fsg0zT2KoqD4Kkul + Uv54PI6ysjKp1GbL4/HUHz9+/BOPPPLIX87MzND29nb09PQYkiQlLMvyHT161L93795wNpu99tprr/2x + z+e73jTNHbW1tfkTJ04ETdO8JRAIfJ5zPkkIwcTEBDKZDCRJwsGDBxsPHDiAhQsXRisrK49JkhTK5XLe + iYkJKIpCy8vLUVZWJjPGvBMTE6ipqcHy5cvh8/lOC+8r1cyuq6vrF0IYtm1PRyKRwuSSJPT39+PIkSNg + jGHZsmWoqamBYRgIBALo6+vL33333eKSSy7BunXr0hUVFccDgQCi0WjQNM0NHo/nS7IsI5PJYHp6Gpxz + ZLPZQCwWa5mcnMSGDRteFEIYhmFEpqamkE6nPb29vZ/M5XKbAbzo8/kQDofBOffk83l5cnIS+Xze4ziO + 151ZrkC7nAM45zql1PJ6vQiFQj+vrq7+Sk1NDTVNEz6fzwwEAm9JJBKf3LZtm6yq6nWrVq26G8A+Qogo + PeKWhLC4vxylFE1NTZiZmfnwnDlztldXV/vj8TgPh8OhpqamT7/44otrnnnmmSvXrFnz1mAwuEuSJNi2 + fdPRo0c/feLECXXp0qX9K1as+PdIJLIvEAhYyWTSBrCwq6vrvx5++GGf1+u99O1vf/u7CSF3TU9P2yUr + 0zRNbN++vaG1tfXTFRUVH81kMsNF8dcBmLIs+zRNg23bEEIgGo3eNDQ09P50Oo2FCxceuOWWW/75wx/+ + 8Kht26YkSfaJEyfmfeELX/jXTZs2NTz33HOXXH/99W8KBoNPLV269Be7d+9+z/PPPy97PJ7msrKyg47j + oKmpCS0tLVBV9V2PPPLI6mJlvp8mk8knvV7vZUIIs9iLURSjUTgAp5gp2dfR0fGpcDg8KYQ41W1BDMPg + mqaNSJI0BeAjwWDwi5IkEcaY1d7e/vVYLLY8EAigp6fnn4LB4AOmaYbC4TAdHh7u13UdpmnC6/WipaVl + 18KFC/dv2rRp0YsvvhhcuXJllWVZk5qm4YorroAkSYFkMvmhBx54QCsrK8vncrkvaZoWk2VZLo3bjh07 + glddddU9VVVVf/rcc8/tsW0bixcvtmVZ5l6vF7IsO4QQ251ZrkC7nCNKj8Uej2fCcZznk8nkbExwLBY7 + MD09rZeVlX22v7+/8/LLL/8cgLcIIXjpfSUr7xRfJyRJQjQa3fvII4/svuaaa6BpGhRFQXd3dzqZTH5t + +/btSyYnJ6+YO3duD4AjR44cedPevXu1zs7O2IYNG74pSdK3dV2HJEnI5XIQQuxftGiRPT4+/sP9+/d7 + Dx48+FFZln9kWVaqZHH6/X5ks1k89dRTt1122WWP+/3+7xZD7UjpuCzLQnV1NTo7O7tffPHFNx89ehRt + bW3H//zP//xzt99++8aKiorZcamoqNj9D//wD/rQ0NDPpqam/I7jrNm7d+8ThmH8R2Vl5fVDQ0PVmqZ9 + Yvv27YcGBgYGr7vuOjQ1NSGbzd6QTCYbFi9ejKqqqoGZmRnU1dXJpWM4xUKe/Z0xljFNc9vQ0NBo6WZX + 8g1nMhnU1NSUalj3m6bZb9s2PB4PfD5fmjEGRVEQj8cP27a9z+PxzEavAMDAwAB+/vOfQwixVQjxg7Ky + sn9PJpOXmKb58Y0bN368trYW1157LRhjlfF4/HZKKS699NJ8Pp/fzRgziusIUFUV2WwWR44c6erq6rrG + cZzeVCpllPo5lhY93YXCCwc3iuPCsaShKEpQVdVQLpebXUCbmprK6bq+denSpTAMA319fS3Hjh2DEEKc + bR4WO3f7pqamsHnzZoyNjZVik7e0tLRsDwaDsCxrldfrXRUIBBam0+krLctCW1vbC8Fg8K7SIh/nHJqm + oba2VlBKn1myZMlzQggcPHiwcmJiIli8MQjOOS677DKxZs0aDAwM4Pjx459VVXVtyT976s1IURQEAoHL + OefXKoqCdevW7Vq3bt3GioqKUhG52e9tIBB4LJFIXL9+/fobTNO8Rwhhx+Px/jVr1uQDgQBOnjy57uTJ + k5FYLIZkMgnTNH07d+4si0ajKC8v39ra2rq1vLz8NN/+yyDFVPaQ3+9fYBiG13GcMsdxyh3HqeCcRwzD + 0Eqhf4ZhzHZFlyQJjuOQ0g1S13VMTEwgm82eVlRqZmYGvb292L17Nyilo6tXr0YikVD6+vqWT05OYnR0 + FPl8HhMTE43PPfdcGSFENDU1fa+joyMtSRJzHAeWZWHt2rW4/vrrMTIygueff/5j6XR61cuLV5XOyZ1R + rgXtcm6YrZ0pijiOA1mWUV1djXXr1kFV1Xwul8vm83lfX1+fFA6HS5bSq01OoaoqxsbG0N3djYaGBliW + hbq6OqempgbxeBzxeFytrKxsdRynNRwOQ9O07LFjx0zTNBEMBtHU1ARCCPx+P4QQGVVVt0uStD6RSGir + Vq1ap+v6pmw2y23bRl1d3fHu7u7tvb29G7Zu3drQ1dX18crKyn7DMGajHQghmJ6expEjR3zRaBTV1dW4 + 5pprzKVLlwIA6+/vd372s59VdXV1LZFlWUxOThqrV68eaW9vx8jIyGB5eTkikYhVUVFxtyRJn965c6dn + 6dKl81atWrWzqakJoVDoI1NTU2sikQgikchPJUnaeZZ61oRSSmRZRiwWq3vhhRf+Rdf1qWLUBgDAcRyZ + MZaoq6v7T0LIs47jCFVVZ9cJTokuAaUU+Xwek5OTKN0UamtrcemllwIATNNEXV3dnpmZmcenpqauHRkZ + Cd94441tXq93oLy8vMayrE/GYjHS0tISf/DBB78hSZKzaNEitfRZjY2NU5FI5Gd79uy5evv27R2XX375 + VxcuXHiHruuT52VTBRdXoC8mgT4Vxhj8fj9CoRA0TdP37NkTt23bZ1kWCwQC2lnqW5CXu09kWYbH40Eg + EIBt21AUBZqmIRaLIZVKCZ/PpxqGMesGSaVSEEIgn8/Dsiw0NDSU/MaO4ziJouUol5WVdU9OTv5SCMEB + wDAMEQwGv3nddddVP/TQQ/WbN29+8+rVq3/S1tamE0J0AKGSiMViMWSzWXi9XlRUVOSKj+m24zitW7Zs + +dS3vvWtSymlXJZlJxwOi2PHjqkrVqzYpyjKX8qynLEs687Ozs47tm/f3tbZ2fnRzs7O7clk8ujQ0NCa + WCzmX7JkidnR0TGm6/qr1QEhkiQhnU57Dh48uLRUFKo0dsUsSd1xnMcJIc8RQmxFUc5YX4QxBtu2MTAw + gLq6OixevBh+vx+pVAplZWUQQhw1TfNHDQ0N1xqGsXjDhg3/R9f1D2az2fDg4OA1mqZhxYoVI/fdd1/c + NE3MmzePlFqDWZalyrJ894033lh3//33dwwODi6ZM2fO1S0tLQ8HAgHr1IgdF1egXc61UheYnfyWZcEw + DADwAagpiofd39+fX7Ro0Wvd52xn7H379uHQoUNYunQpTvGhomQRllK1Q6EQFEWBrusYGBgAIQR1dXVg + jBEASjEz0DYMY1gIYZVuCoSQcG9vbzqfz3+xoaGh8+DBg83Nzc0fnz9//nZKaQxAdck/rqoqZFmGaZrI + 5/OzzQm9Xq+/urp60e7du7s1TYNhGNi7dy80TcOqVasqVVVVJUnKaJqWXLly5ZHe3t62gwcPLg6FQmWK + ooR2794dSaVSYIzdMz4+vkWWZfj9/rO6gkzTRG1tbWrhwoVP6Lo+cIoFTTjnEiFkRpblp0tx0q9mrTLG + MDMzg7a2NrS3t+PQoUPI5XKorKxEcTFzaNGiRePPPPNMzYEDB+a3tbUhHo/P37FjB5FlOes4zpcXLVqU + IYRAlmXYtl26lsEnn3xSpFKpf6ivr5976NChuTt27PjrhoaGJGMsdao17+IKtMvvx5qmjDFIkjRbQCib + zSrj4+OKz+dDVVWVc+DAgbMWHyphGAbJZrNYsmQJGhsb8dhjj6Gvr29WQFpaWlBZWZlXVXVEVdXpVCpV + Ho/HWWtrayl+GbZtIxaLob6+HgCCHo/nCsuy4PP58o7jPMs5zxYzDgEAwWCQ9vX1bZ47d+69qVTqo4cO + HeqJRCKVuq5rpTjv6upqzJkzBxMTE+jr68N///d/y+Pj4/jABz5AAoHAyerq6s/EYrEFjLF8eXl5/Zo1 + az5x4MABVkoDHxkZASHEoZR+1efzLdm/f39dV1dXV3d39zunp6eXLliwAOFw+Nnjx49PNDU1IRgMnnGM + Sinnfr9/fM6cOV/NZrMvSgXHLiGEkOKThEEIsUtCWSocdSah5pyDMTbrs5YkCbquY2yskGukqupuIcTd + lmV9+sUXX9Q6OztvYox9XNd1rFu3biYejz/Y1NQkQqHQ7D5K1NbW0h07dhy88sor787n858+ePBgY2Vl + 5QfS6XT45f5+F1egXc6NKIMQAtu2c47jZDRNm+147fF4PKOjoyt2794Nj8eD7u7ufq/Xe1aBKP29p6cn + 6zgO5syZA5/Ph7a2NlRXV78pGo2un5iYQHd3945sNrtrfHz8qGVZD1uWdfvk5OSG7u7uD9q2/V+lYkfF + +GrkcrnW3bt3r6SUYvXq1XYkEplMJpP81LC01tZWcuLECdTV1X3t+uuvv/T+++9f9/jjjzcwxlBWVjbb + lDYUCm0LBAJ74/H4kq1bt65ZvHjxrQA2hsPh1Ic+9KFf1tfX/7K4y65du3Z9fP/+/azk5+WcY3h4GJzz + J5csWRLfsmVLXSwW+9ixY8fqZ2Zm5EWLFvW1tbUNTk1Nwefz4SyP/qS0T8uycqlUakSSJKv4VDA7jrIs + n5aA8vI07Vca/1P/TilFNpudXfjlnGe8Xu/WJUuWYGBgoDMWi/1Lf3//Ao/HI+bNm/d8LpezT3l6Os2d + snbtWrJt2zZIknTn5ZdffvnGjRuv27p161Kv14tSNIeLK9Au59a1ASEENE2rrqysXCiEiBNCvD6fry2d + Tt+8ffv2G6enp7Fy5crjlZWVX6yrq4PjOPTUynGnZhyXoi96eno6w+HwYCaT8QIIr1ixomd0dPRvDx48 + OL+xsRFdXV3PzMzM7Comavy6p6fn9qNHj1bW1tb+n/nz5+uWZW2ilE6rquqXJOnWffv2/emzzz6rRiIR + OxAI/E8ymUxzztkpj9bCtm1MTExgcnJyoqGh4RuVlZVNJ06caHYcBxUVFbOLavl8/tfd3d0PDA8PLxkb + G2szTfPLzz//fCiTyWyanp6OLVy4UG5ubl52/Pjxd33/+9+fTW4hhKC+vh5Fa1zU1dX9eteuXXP37t27 + qJjAA7/f/y1VVX9dXV09a9Gi2G725XVLSmMvy3IoEoksB0CFENIpN05CKaWZTCbGOY+V0rNfLsyleiKO + 48wWfGKMXeX3+29gjD3hOM5jJfdRdXU1wuHwUDgc3nngwIHlmzdvXhCNRlFXVzfKGPvnyspKg3OOkZER + vCxlHvl8HkuWLEF5eXmutbX1X5cvX964efPmBblcDsFgEG6EnSvQLudWnD2cc9kwDAwMDKw2TfM/DMNI + APBKktQejUbbjxw5gnnz5mUXLlx4Xzab3UkphaIopJTwoes68vl8ye3hFUJgbGwMmzdv/ltN026zbZsA + KLNte8Hhw4dDQgisWLFiVyqV2jQzMwMhBJqamh6trq7+XiKRuGPLli2t09PTX/L5fOvz+fzgyZMnawcG + Bm7du3dvJBwOY9WqVY9Eo9F/tG3bEUJ4LMsqiaAPAGtra8Ovf/1r7Nu372dXXHFFD+f8E0ePHqX5fB62 + bePIkSM4fvw4AoHAIw0NDVem0+n1X//611sfe+yxL8qyfFkikRjSNM3v9XrXRKPRldFoFHPnzuWMsccd + x9E556ipqSlZ01+sra3dsGfPnnmO4+Caa65Ba2vr8ZeLJyFEJoQoxXRuQggBY4wJIdTiTaXuueee+7Rp + miMo1FoqWcNECKEEg8GTlNK/AzDd0dEBTdNOq31tmiYymQz8fj/8fj8IIa2jo6Mf2bVr141VVVXdbW1t + xwAcTyaTOHDgABhjB3Rd/2o4HP7hgQMHEAqFsHr16hlN0446joPx8XFks9nSk9VsrLtlWWTFihXIZDKY + mpr6dWdn53eOHTv2uf7+/rJMJgPOucQYc+e9K9Au5wIhRDoUCum1tbXIZrMVhw4d2lAqs8k5RzAYxPLl + y+ORSORLPp/vzlLpTiGEYIwZVVVVnkgkgkAgAABwHCdVWVmJRCKB4eHhHs55T2mSS5KE+vp6u6Oj4/lU + KvXJHTt2bCv5hA3DGG9vb//aW97yFu/mzZvXDA0NNWQymTtKfmOv14uFCxfqVVVV3+/s7LxzeHg4FwwG + Zxcza2pqIElS0rZta/myZdh39AgmJiZQUV397Yb6+lX5fP7ycDiMqqoqxGIxHD16FIlEYs+SJUs+ev31 + 139m+/btayYmJiqz2eztsiwjHo+Dc45IJIKVK1cO1tfX30cI+ZZt21kAs4/zlNLhpUuXTqZSqXmxWAyR + SGSjbdsH9HweQlHAZRmqEPA4ju3z+ay6ujroui4cx4FpmrYsy3pdXR0URVEPHz68FMDSl6d627aNjo6O + rCzLn5FlebrktijeEPPhcBjl5eWzL7/fD0ppzcDAQOWuXbtw6aWXRnp6emoopccnJyexZcsWAEBjY+PA + ypUrc7lczltbW5sOhUI/zOdytmXbyBMCtbwcXkLALQtVVVVgjBmcc9syTaQ4R84wEJKlby1fvvxSIcSf + GIYBWZbNfD6vuzPLFWiXcwDn/EFCyJiiKEsVRTFKouD3+0V9fb3o6uoC53zLgQMHflRMXEEoFEJdXV08 + l8t9XtO0Rc3NzdOBQMDgnMMwjO8qijKuKEpAURS7uD9RU1PDm5ubaUdHR3R6evreHTt2HA0EAohUVIB6 + PEjaNqYMY6+vquoD11177c3H+vqunJycrLRtO6DrepYpyuT6N71pMD46+s8JwzDSnKOsMgKP1+tIhnFv + 76FDzUKI53NCDOfzedzU1gkCCQ5lJwM9S+5qj44N8VxeaWlp2d3e3g5CCLZt24Z8Pr+7qqrq/W9+85vf + 2tfXt2JkZKTCsqwwIcT0+XzTra2t4zU1NU8dPHhwk+M4XJYLHVQymQwCgQAURUFDQ8M3g8HgkWAw6OOO + 8/X+0dHBtGni8ooIlgXD2CM4JjjvJcB3q6qq1hm6/sxkIoGZXG6GqerD4dpaxUomecnv/7LrA0qp3Nra + mpRlOTM0NASgkNVXXAz8f4FAYLi+vt7f1dXV39/fj1Qqhaqqql5Zlu+uqqoaUhTl2Vw2e4gzhtQpC36K + pvWX1db+3/Ly8mXNzc0Dk1NTd6UE534m4e31jZAsCzs01TYd5/s+n6/d9Hh68x7PkAGBJcNDqNb82KZp + Tvvixf+TmJ6Ox6any7xe7xOZTKbfnVmuQLucAxzH2RqLxbaePHnytL9XVFSgpqZmNkROURTs378flmWh + WHMhbdv2V8bGxmCaJizLwujoKMLh8C+j0egvJycnT9ufqqqora1FLpeDqqrweDwghKC1qwttHg/qOBCJ + xTGWyya2KMoPLEn6Uffy5ZXlkUjkcO+h1MEnNw915nWMSxKuUDzQg2GEwUCzuvU8xf89EY2i29BxOaFQ + ObB2+SWoamjBdoliajj6s+eO9v9sOOBFvW3ByzmqqqrQ3t6O+vp6OI4zEY1G74pGo9/u6ekpD4fD1YZh + 5MfHx8d0XdcppQgEAmCMldKtMTY2hlMSRjZGo9GNUD2ICI51Aoj4g1ivejHHAeocB4+rIvpkMvG16OjY + 19bNmYsby8pR5gib+Xy/WCUrv7i7/8yaJssyFixYAEop9uzZg9bWVpSVlcE0Tciy/POpqamfRyIRCCEw + ODgIx3FQVlaWnJ6e/s7o6Oh3qquqYDCGShDcUlmN+gWLIfJZtEcqpzypzH9+f2QEoaYmVBOCayQNIU3D + dRaHFE+AVkfszRSfG4pG8c5jx7DEEkiXhXHZwCCq2jtROZHAs6bz+InY1OPp+DQizYXuMi6uQLuciwsk + SaipqUFzczNkWYbX68XIyAj8fj9kWcbg4CAuueQSaJqGX/3qV1i9ejUcx8HAwACamprQ2dkJRVFgWRb2 + 79+PSy65BF1dXQiHw7NJKZRSNDY2wuPxlMQDgUAAuVwOadPAovJKXO8LAC++gOHoKMRl65EPhZ0Ax7g3 + lRqPcIEFlOKtugX/ySH8SXUD1Gof4FgQMzOQrDwSLW1Y4Q/iBkcgAAqYFmCaWEk9wMAYRrI2jjRUoEfy + IEqB8gULsGTJEmQyGTiOg8nJSfT29vK5c+fG0ul0rLToRgjB4cOHUVtbC1VVZy3X0mIj5xy246CpuRkL + vH4sKKvAzSkdLb4ywOcDdB3dpg2WyWM8Uo04kXDHkh5cqmo4OjODmDeA8nkLkRgegTwzDVJdA4xHIdU3 + ID3Qj02cQxCCQCAw+5klt04ul4OmaWhpaUEwGEQ2mwVjDBMTE5iYmEBlZSUaGhpQWVeHKkJxlaBY2TEH + WLgEiI4Bk1EcoQp6axvQUVmJuZoPb3MU0EAQgAAoxdVcwMykEauowG19fWgfzwCXrgRUBZAlrN7dB62t + AeOMQfj9aC+vADdNd2K5Au1yLvB4PFi2bBk6OjrgOA4aGhqwY8cO5HI5SJKEvXv3eleuXFnmOE4CQLaU + 5WZZFlRVxQ033DBbB4IxBsYYbrjhBti2jaGhIUQiEYRCIWQyGcRisdOiDEr1iwEOcAdgDI1Mwvs8PqA2 + AOQzQDoFyBrAJA16vuxde/dG0XOJQNEdQByOW0ZGcctVbwJ8AUDPA4QAjLUBJA9gHNwRHwxXAxX1gBbE + k9zEz20LsO1StMPseOTzeXDOUVFRgYqKCmQyGTz00EN4+9vfDk3TTit0BGAxAJLNZg+vv+Jy/QOKHy2E + ALnjgG2VmsgGAcG6Mnrq3+YucuBTAcsCdB1zCMEc3WCob2jaeOVVfmx55gCuuhp47BHghhuRuOtO3G4a + eNbjQUVVFZLT07/R/YRzXr1q1ap0LpfLGYYxO7a6rqOjo0NasGBBHbxec6mD8ZU2ANsG8nlA1wHD0OaG + q4P3LFoxgfY2gEpALAUIXhhDIUAcBzeNjeGm+fOB48cLx164S1BQ2gxFTiwhbOartgNEqoCGFiCddieW + K9Au5wIhBLNtu9a27Q2GYWTj8fiB7u7uqUAggKeeeorYtj3HNM0/0zTt7oqKilwoFLpUluVxSukTACpN + 05RlWR4H0MA5nxBCVFmWVWsYxoTjOAsMw3Dy+XzUcZwyzvlBABnOuRYMBtsBLPSonsOCi20ACcJxVNhW + vPCz7QPnS8EkDYw8BlWphGG8D0L8M4AOEAwXzDzkQYgFx2mGEMMgpAzcmUTO+BgcexeE8n14PWtQXdEN + j/o8OD9UFB8/CLxCiBnHcWS/3++vq6trlCSpmjH2tCzLVYFAYCqdTjcIIfoJIVWMsU5CyF5K6TwhxACA + LwHIEkI+aZrWDKdmClSqhaKsgSQ5AH4BxtbD41kOYfwUtjUOkwCFzjMVoHQEjPnBnf+FTLoRXLwbplUH + IWIwzfqwEP3fBuyrAwFiCtElSdKq5ubmMVVV+zjnuhAiwzn/G9M0H+WcbxVC0PLy8kuFEAsppc/Ztp0m + hHx8YmryyLjFv4aq+gi44wEhSRAo4E4jVPm98HnuAud5MFIFgf0QqABBPShbDEKOgZDnYZkOqmveBFuJ + QJL2gosjSGf+Bpnsk+D81yBEAucSuOMFd1IApt3Z5Qq0y+sX6C7Lst7T2dn5v8vLy7cPDw+PJZPJXbqu + W6qqKpqmHR0fH7+9p6enb/369fbg4ODXPR7PvZZlvSCEuEMI4TMM478AfFJV1f+klH4QwM0ADq9atWpu + Lpcrn56eHjdNs5Uxdo/H4zngOE5TT09PD2NszvB0/DGH8+PgYjWCoRtgmC/ANFdAkirQ1n4NfF6Offvu + xNx5WxGduBa1dc+C0juhG89D0xJg9Aeg8giy+S/B638QEGth29/BwIkOeL0G1PDbcMmSv8Pla5dhZPzx + ifHJTwwqZL+iKa2C81sppUIIoXd2dtLa2tpVsVhMDgaDYwDe4TjOvYSQz/h8vjts214bj8e/VF5e/uFM + JvMVxtg/5vP5RlVVLUbpEkLIPBByDzj/CBYv/CvouoV4/J3gfBEk9n+gqgKE6iBkEMAQgDeD0q/B79eg + 6+sQn8mhpnY1JOlt8Pu/A6Z8QQQr3rstHp3KO05AZuxLPStWXNLW1jacTCafsW17WJKkWCwW+1Ofz3fE + 6/Vu5Zxf2dnZ+ckrr7yyi3P+bxMTE4YQ4o4dO3b+t5Qz8J633HYluHMVbOyBI1qhqAdA8B60N+vIZ3PI + 5N4Dj+ezIFgLWe7BwgUtiE0eA6FvgdfvQ3vDXWhprUM6/QQilf+BsehyUHESFeH56Of7ANSC0GWg9FkA + 97uzyxVol9eJZVnXcM43zJs3r3fp0qVbH3744avj8filmUwm19LSMlFTU1OdzWaDVVVVf7ZmzZpv3Hvv + vb8yTfNeRVEYIWS5bdu+eDweqKioWNLS0tKuadoy0zQHKaUrrr76avPQoUNT27dvVy3LsiORyF/Ytj1i + 23aorKxsu2EYd0OgUZLldjhOCG0d70VD8w0w9Gp4tTTmze1Fe1sZBk/+Bdq7lmN4uAmrV38aTKpBLv8O + ZHNZELoRgTJgZqYbfl8bFLkBlHoA0gLN2wGP9m60tUURCv4YeeP6bwz2v+UJU+y/KdQoZ03jimAwuE5V + 1WHbtmMAFgghjsiy/F4ATYZh1ACYEwwGZc65OTk5WcYY+3wikaivqam5Vtf1csdxqlSP5xpBaRACCmy7 + BnPmnMT42HFMTd4I01oIy1Th890CzWMA+AGAJGR5PhxeAdNugW6sQFPzINo7PopcpgKrVzVB1eYc7lmn + fHJwD6aSM36fLC9bsWLF/ieeeAIAliqKUkkIqYlGo3Wapv1JIBB4jDH25mQyadbU1HyosbExu23bto8d + OXLEo3rUmlp/EMjnfCD4M5jGBng8FL6mdswk/NC0d0CQFxCLa6ir+ydQWgePZxo9i3vx+C8pGAugrmkR + Jqf8WBnZB4mtQNfcb4IO1KEraKG1oRy9oUkEgosBPh+MHHJn1oUB++xnP+uOwnnMY489pjHGGm3bnrNv + 376xgYEBjVL6POf8F16v98ZgMDhH07SJ4eHhwYGBgX26rpfpuv4iISQqhLiUMXaZYRi9iqLUV1RULJMk + qSWTyXxZCOHnnM8bHBy0U6nUw16vdySRSOwbGhryc84nbdvu1XX9EpuSRa0cW7sItWHbGoTzQ1CqQDdn + kM2pSKU8GB4VoCyMhoYxGEYzGDuJePzHONZXCe5sRC4xg8qaayFJd0GSZFDpOtRUzwCQ4Dg55PQ4Bk96 + RTyefJ47vdOKsrNalsoEUJtOp59OpVJDXq93maqqiXA4bDHG5hbjvOfOzMxYO3bs+MGCBQsaa2tr12Uy + mdq6urqUpmmtXq93LJvNxiVJ6uDAyFKbP1jOsQbZzFwkEznYvBKmJSGTm4CiVEJRPLCsH4PzGVByM2S5 + FTm9AYRUoa4uDYe3glED/tDctKHLD3ulbf65HdPpZMppbmq6wbIs79jY2DzbtpOMMYlz3haJRMYCgUBX + Mpl8PpFIRFVVvSwajTYcPnx4cTweb6eUJhrb2gJrGxtjXZQ5ENwDQe6Bz3sFPGolPOogMlk/fN4gTP3H + SCS6EAxlQEkSuu5Bf38d8tlHkMnG4fPeBA6CbDaJXL4S1VUT0DwR5A2K9k4vqqrXIpveBdN4Btdde9Kd + Xa4F7fL6eY4QkpiYmIBt2/FsNvsdxtgQY8zM5XKTmqZNU0qfFULMi8fjJJfLHYzFYtFwOJxWFOWu6upq + EggEAslk8p/Ly8s/CuB/hBCPzszMPPnkk0/eU15enlMU5UtCCO+mTZuSc+bMWeD1ehOjo6N1nZ2dkXQs + dm9KtzZBC1ZBontBeBST8U0QJABN/UvEYiryxvegyDkQcgyKejWm4woeuv8JvO0dL2DPjkHs2T2Jd/7l + p6AoB2GYvWDyz8HYL6HINchkvIhNrYTPv/bpxPT3k9wur7PFdUPDw496/f5vbt26dcQwjPo3v/nNO+Lx + eH8wGEwBCKuqulhV1fW2bf+9ZVn62NjYHr/f/96KiopwNpuNCyE6VVV9dHx83Kqqrr7V4vykJXunoHi+ + i+mkDDjjsO2ncOxEL44OjGD5wjehpakK6cwzUCkgq/fB69sAxemFbV8G7pjQjdUQPAyP5y2mwv71eGVo + meo4ng2XXfasZVkfGRgY+CsATyWTyZ/6fL5aIYTu9XpfIIRcum/fvqGBgYGn1q1bV9XZ2blB1/U909PT + d1JKd3pVtd0yLBUgMwA5DIf3IW1E4fMBnG+CEGswOTUfT2/eiOtu2Ag9L8Mya2A570R5xW5kEvvw4AM5 + 3Pb274PzdiRmHoIgKRC6H4JXwdArwdgtkOXdMMld4DjiTitXoF3OkZcDwD4A/wuFWhFZFBbfAOCnKEQr + 2AD2oFAnwjmlUM8+AB8lhHAhhA7gQwAMAIYQIhOPx//K5/OlPR7PlOM4pdoSk8W+APsAPC0gdBSKA40W + nOIAhEij0NXkEygUQsoUHeYA8FDx/xxQuhGCcxTqQW8tvnsHgJ2A4BAiVvzbPgDfo7KcPbnvQLB/8CQa + 2ttNf3d3PwpNBQYGBgYGt23b5r/pppu8mqbtQ6Er9fcBJItjMQlgghBCi/vcBcApdj6/t/gzB/AigIMA + OIA8uBBwOCDE08XyGqV+ff8PwA+L45UvjvMjACgYe0jPZvJHDu4rb+tZmpQYs7nj7AbwNwDs4lgfBOAU + j+VpzDZhx/cIIfcRQszideAAjgsIAIQDGCu+5xHMdo4h2yDETnDHBEhpcY8B2AxCjOIxAoR8pTin84XP + FgDENIDDEGJ34RxItnjuLhcAxC0/6OLi4nJ+4lbwdnFxcXEF2sXFxcXlt+H/DwB6XCzBenyyuQAAAABJ + RU5ErkJggg== + + + +
diff --git a/addons/31_thanhtoan_kl_dg_cp.rptdesign b/addons/31_thanhtoan_kl_dg_cp.rptdesign new file mode 100644 index 0000000000000..3968a546d9dee --- /dev/null +++ b/addons/31_thanhtoan_kl_dg_cp.rptdesign @@ -0,0 +1,7747 @@ + + + Eclipse BIRT Designer Version 4.4.0.v201405191524 Build <4.4.0.v20140606-1451> + + + queryText + 2829 + + + queryTimeOut + 2829 + + + rowFetchSize + 2829 + + + queryText + 2833 + + + queryTimeOut + 2833 + + + rowFetchSize + 2833 + + + in + /templates/blank_report.gif + auto layout + ltr + 96 + + + static + true + date + true + + simple + text-box + + Unformatted + + + + static + true + date + true + + simple + text-box + + Unformatted + + + + static + true + integer + true + + simple + text-box + + Unformatted + + + + + + + + metadataBidiFormatStr + ILYNN + + + disabledMetadataBidiFormatStr + + + contentBidiFormatStr + ILYNN + + + disabledContentBidiFormatStr + + + org.postgresql.Driver + jdbc:postgresql://localhost:5432/LAMTHAO + postgres + TmluaEtodW9uZzEyMzQ1Ng== + + + + + + + day + dimension + day + day + + + month + dimension + month + month + + + year + dimension + year + year + + + + + + + 1 + day + string + + + 2 + month + string + + + 3 + year + string + + + + LAMTHAO + + + 1 + day + day + string + 12 + + + 2 + month + month + string + 12 + + + 3 + year + year + string + 12 + + + + + + 2.0 + + + + + + + day + 1 + + 12 + 2147483647 + 0 + Unknown + + day + + + + day + + 2147483647 + + + + + + + month + 2 + + 12 + 2147483647 + 0 + Unknown + + month + + + + month + + 2147483647 + + + + + + + year + 3 + + 12 + 2147483647 + 0 + Unknown + + year + + + + year + + 2147483647 + + + + + + + +]]> + + + nulls lowest + + + phieu_dn + dimension + phieu_dn + phieu_dn + + + hopdong + dimension + hopdong + hopdong + + + phuluc + dimension + phuluc + phuluc + + + daidien1 + dimension + daidien1 + daidien1 + + + chucvu1 + dimension + chucvu1 + chucvu1 + + + daidien2 + dimension + daidien2 + daidien2 + + + chucvu2 + dimension + chucvu2 + chucvu2 + + + cv_kh1 + dimension + cv_kh1 + cv_kh1 + + + cv_kh2 + dimension + cv_kh2 + cv_kh2 + + + khach1 + dimension + khach1 + khach1 + + + khach2 + dimension + khach2 + khach2 + + + dv_vchuyen + dimension + dv_vchuyen + dv_vchuyen + + + day + dimension + day + day + + + month + dimension + month + month + + + year + dimension + year + year + + + + + param_1 + tu_ngay + date + 1 + true + false + + + param_2 + den_ngay + date + 2 + true + false + + + param_3 + dv_vanchuyen + integer + 3 + true + false + + + + + + 1 + phieu_dn + string + + + 2 + hopdong + string + + + 3 + phuluc + string + + + 4 + daidien1 + string + + + 5 + chucvu1 + string + + + 6 + daidien2 + string + + + 7 + chucvu2 + string + + + 8 + cv_kh1 + string + + + 9 + cv_kh2 + string + + + 10 + khach1 + string + + + 11 + khach2 + string + + + 12 + dv_vchuyen + string + + + 13 + day + string + + + 14 + month + string + + + 15 + year + string + + + + LAMTHAO + + + 1 + phieu_dn + phieu_dn + string + 12 + + + 2 + hopdong + hopdong + string + 12 + + + 3 + phuluc + phuluc + string + 12 + + + 4 + daidien1 + daidien1 + string + 12 + + + 5 + chucvu1 + chucvu1 + string + 12 + + + 6 + daidien2 + daidien2 + string + 12 + + + 7 + chucvu2 + chucvu2 + string + 12 + + + 8 + cv_kh1 + cv_kh1 + string + 12 + + + 9 + cv_kh2 + cv_kh2 + string + 12 + + + 10 + khach1 + khach1 + string + 12 + + + 11 + khach2 + khach2 + string + 12 + + + 12 + dv_vchuyen + dv_vchuyen + string + 12 + + + 13 + day + day + string + 12 + + + 14 + month + month + string + 12 + + + 15 + year + year + string + 12 + + + + + + 2.0 + + + + In + + + + 1 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 2 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 3 + + 4 + 0 + 0 + Unknown + + + + +]]> + + + nulls lowest + + + stt + measure + stt + stt + + + sl_haohut + measure + sl_haohut + sl_haohut + + + loai_sp + dimension + loai_sp + loai_sp + + + thanh_tien + measure + thanh_tien + thanh_tien + + + don_gia + measure + don_gia + don_gia + + + name + dimension + name + name + + + dv_nhan + dimension + dv_nhan + dv_nhan + + + + + param_1 + dv_vanchuyen + + integer + 4 + 1 + true + false + + + + + + 1 + stt + decimal + + + 2 + sl_haohut + float + + + 3 + loai_sp + string + + + 4 + thanh_tien + float + + + 5 + don_gia + float + + + 6 + name + string + + + 7 + dv_nhan + string + + + + LAMTHAO + + + 1 + stt + stt + decimal + -5 + + + 3 + sl_haohut + sl_haohut + float + 8 + + + 4 + loai_sp + loai_sp + string + 12 + + + 4 + thanh_tien + thanh_tien + float + 8 + + + 5 + don_gia + don_gia + float + 8 + + + 6 + name + name + string + 12 + + + 7 + dv_nhan + dv_nhan + string + 12 + + + + + + 2.0 + + + + In + + + + 1 + + 4 + 0 + 0 + Unknown + + + + + + + + + + + dv_nhan + 1 + + 12 + 128 + 0 + Nullable + + dv_nhan + + + + dv_nhan + + 128 + + + + + + + sl_haohut + 2 + + 8 + 17 + 17 + Nullable + + sl_haohut + + + + sl_haohut + + 25 + + + + + + + loai_sp + 3 + + 12 + 64 + 0 + NotNullable + + loai_sp + + + + loai_sp + + 64 + + + + + + + don_gia + 4 + + 8 + 17 + 17 + Unknown + + don_gia + + + + don_gia + + 25 + + + + + + + name + 5 + + 12 + 500 + 0 + NotNullable + + name + + + + name + + 500 + + + + + + + thanh_tien + 6 + + 8 + 17 + 17 + Unknown + + thanh_tien + + + + thanh_tien + + 25 + + + + + + + +]]> + + + + + stt + measure + stt + stt + + + dv_nhanhang + dimension + dv_nhanhang + dv_nhanhang + + + dc_giao + dimension + dc_giao + dc_giao + + + kho_xuat + dimension + kho_xuat + kho_xuat + + + npk + measure + npk + npk + + + lan + measure + lan + lan + + + khac + measure + khac + khac + + + lannc + measure + lannc + lannc + + + tong + measure + tong + tong + + + thanh_tien + measure + thanh_tien + thanh_tien + + + gia_cuoc + measure + gia_cuoc + gia_cuoc + + + kho_xuat_12 + dimension + kho_xuat + kho_xuat + + + + + param_1 + dv_vanchuyen + integer + 1 + true + false + + + param_2 + tu_ngay + date + 2 + true + false + + + param_3 + den_ngay + date + 3 + true + false + + + + + + 1 + stt + decimal + + + 2 + dv_nhanhang + string + + + 3 + dc_giao + string + + + 4 + kho_xuat + string + + + 5 + npk + float + + + 6 + lan + float + + + 7 + khac + float + + + 8 + lannc + float + + + 9 + tong + float + + + 10 + thanh_tien + float + + + 11 + gia_cuoc + float + + + 12 + kho_xuat_12 + string + + + + LAMTHAO + + + 1 + stt + stt + decimal + -5 + + + 2 + dv_nhanhang + dv_nhanhang + string + 12 + + + 3 + dc_giao + dc_giao + string + 12 + + + 4 + kho_xuat + kho_xuat + string + 12 + + + 5 + npk + npk + float + 8 + + + 6 + lan + lan + float + 8 + + + 7 + khac + khac + float + 8 + + + 8 + lannc + lannc + float + 8 + + + 9 + tong + tong + float + 8 + + + 10 + thanh_tien + thanh_tien + float + 8 + + + 11 + gia_cuoc + gia_cuoc + float + 8 + + + 12 + kho_xuat_12 + kho_xuat + string + 12 + + + = ? and dn.thanhtoan_denngay <= ? + group by pvc.id,pvc.name,rp.ten_day_du,rd.name,st.name,ct.thanh_tien +)A +left join +(SELECT id,sum(kl_npk) as NPK + FROM + (select pvc.id,case when COALESCE(pvc.tung_phan,FALSE)=true then ctvc.kl_dangvc_dukien + else ctvc.kl_vc end as kl_npk + from icsc_phieu_vanchuyen pvc + left join icsc_phieu_vanchuyen_chitiet ctvc on pvc.id=ctvc.phieu_id + left join product_product p on p.id = ctvc.product_id + left join product_template pt on p.product_tmpl_id=pt.id + left join product_category pc on pt.categ_id = pc.id + where pvc.state not in ('draft','cancel') and left(pc.code,2)='20' + )a group by a.id +)B ON B.id = A.id +left join +(SELECT id,sum(kl_lan_supe) as Lan + FROM + (select pvc.id,case when COALESCE(pvc.tung_phan,FALSE)=true then ctvc.kl_dangvc_dukien + else ctvc.kl_vc end as kl_lan_supe + from icsc_phieu_vanchuyen pvc + left join icsc_phieu_vanchuyen_chitiet ctvc on pvc.id=ctvc.phieu_id + left join product_product p on p.id = ctvc.product_id + left join product_template pt on p.product_tmpl_id=pt.id + left join product_category pc on pt.categ_id = pc.id + WHERE pvc.state not in ('draft','cancel') and left(pc.code,2)='10' + )a group by a.id +)C ON C.id = A.id +left join +(SELECT id,sum(kl_spkhac) as SPKhac + FROM + (select pvc.id,case when COALESCE(pvc.tung_phan,FALSE)=true then ctvc.kl_dangvc_dukien + else ctvc.kl_vc end as kl_spkhac + from icsc_phieu_vanchuyen pvc + left join icsc_phieu_vanchuyen_chitiet ctvc on pvc.id=ctvc.phieu_id + left join product_product p on p.id = ctvc.product_id + left join product_template pt on p.product_tmpl_id=pt.id + left join product_category pc on pt.categ_id = pc.id + WHERE pvc.state not in ('draft','cancel') and left(pc.code,2)='80' or left(pc.code,2)='30' + )a group by a.id +)D ON D.id = A.id +left join +(SELECT id,sum(kl_lannc) as LanNC + FROM + (select pvc.id,case when COALESCE(pvc.tung_phan,FALSE)=true then ctvc.kl_dangvc_dukien + else ctvc.kl_vc end as kl_lannc + from icsc_phieu_vanchuyen pvc + left join icsc_phieu_vanchuyen_chitiet ctvc on pvc.id=ctvc.phieu_id + left join product_product p on p.id = ctvc.product_id + left join product_template pt on p.product_tmpl_id=pt.id + left join product_category pc on pt.categ_id = pc.id + WHERE pvc.state not in ('draft','cancel') and left(pc.code,2)='40' + )a group by a.id +)E ON E.id = A.id +group by A.dv_nhang,A.dc_giao,A.dongia,A.kho_xuat +)G]]> + + + 2.0 + + + + In + + + + 1 + + 4 + 0 + 0 + Unknown + + + + + + In + + + + 2 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 3 + + 91 + 0 + 0 + Unknown + + + + +]]> + + + nulls lowest + + + dv_vchuyen + dimension + dv_vchuyen + dv_vchuyen + + + id + measure + id + id + + + + + + + 1 + dv_vchuyen + string + + + 2 + id + integer + + + + LAMTHAO + + + 1 + dv_vchuyen + dv_vchuyen + string + 12 + + + 2 + id + id + integer + 4 + + + + + + nulls lowest + + + stt + measure + stt + stt + + + phieu_phatsinh + measure + phieu_phatsinh + phieu_phatsinh + + + sl_phatsinh + measure + sl_phatsinh + sl_phatsinh + + + phieu_thanhtoan + measure + phieu_thanhtoan + phieu_thanhtoan + + + sl_thanhtoan + measure + sl_thanhtoan + sl_thanhtoan + + + phieu_ton + measure + phieu_ton + phieu_ton + + + sl_ton + measure + sl_ton + sl_ton + + + loai_sanpham + dimension + loai_sanpham + loai_sanpham + + + + + param_1 + dv_vanchuyen + integer + 1 + true + false + + + param_2 + tu_ngay + date + 2 + true + false + + + param_3 + den_ngay + date + 3 + true + false + + + param_4 + tu_ngay + date + 4 + true + false + + + param_5 + den_ngay + date + 5 + true + false + + + param_6 + tu_ngay + date + 6 + true + false + + + param_7 + den_ngay + date + 7 + true + false + + + param_8 + tu_ngay + date + 8 + true + false + + + param_9 + den_ngay + date + 9 + true + false + + + param_10 + tu_ngay + date + 10 + true + false + + + param_11 + den_ngay + date + 11 + true + false + + + + + + 1 + stt + decimal + + + 2 + phieu_phatsinh + decimal + + + 3 + sl_phatsinh + float + + + 4 + phieu_thanhtoan + decimal + + + 5 + sl_thanhtoan + float + + + 6 + phieu_ton + decimal + + + 7 + sl_ton + float + + + 8 + loai_sanpham + string + + + + LAMTHAO + + + 1 + stt + stt + decimal + -5 + + + 2 + phieu_phatsinh + phieu_phatsinh + decimal + 2 + + + 3 + sl_phatsinh + sl_phatsinh + float + 8 + + + 4 + phieu_thanhtoan + phieu_thanhtoan + decimal + 2 + + + 5 + sl_thanhtoan + sl_thanhtoan + float + 8 + + + 6 + phieu_ton + phieu_ton + decimal + 2 + + + 7 + sl_ton + sl_ton + float + 8 + + + 8 + loai_sanpham + loai_sanpham + string + 12 + + + = ? and dn.thanhtoan_denngay <= ? + group by pc.id,rp.id + )b + left join + (select z.id,z.vc_id, sum(z.slps) as sl_thanhtoan + from + ( + select pc.id as id,rp.id as vc_id, + case when COALESCE(pvc.tung_phan,FALSE)=true then ct.kl_dangvc_dukien else ct.kl_vc end as slps + from icsc_denghithanhtoan_vanchuyen dn + left join icsc_denghithanhtoan_vanchuyen_chitiet dnt on dnt.phieu_id = dn.id + left join icsc_phieu_vanchuyen pvc on pvc.id = dnt.so_phieu_vc + left join icsc_phieu_vanchuyen_chitiet ct on ct.phieu_id = pvc.id + left join product_product p on p.id = ct.product_id + left join product_template pt on p.product_tmpl_id = pt.id + left join product_category pc on pt.categ_id = pc.id + left join res_country_diadiem rd on ct.diem_den = rd.id + left join res_country_state rs on rd.state_id = rs.id + left join res_partner rp on rp.id = pvc.congty_vc + where dn.state not in ('draft','cancel') and pvc.state not in ('draft','cancel') + and pvc.ngay_vc between ? and ? + )z group by z.id,z.vc_id + )c on b.id=c.id and b.vc_id = c.vc_id + )C ON A.id = C.id and A.vc_id = C.vc_id +)n +left join +(select a.id,a.nhomsp,coalesce(ctg.name,a.firstname) as name, ctg.code + from + ( + WITH RECURSIVE recursetree(id, parent_ids, firstname) AS + ( + SELECT id, null::int[] || parent_id , name FROM product_category + WHERE COALESCE(parent_id,0) = 0 + UNION ALL + SELECT t.id,rt.parent_ids || t.parent_id, name + FROM product_category t + JOIN recursetree rt ON rt.id = t.parent_id + ) + SELECT id,right(array_to_string( parent_ids::varchar[],',',id::varchar),1)::int as nhomsp ,firstname + FROM recursetree ORDER BY parent_ids + )a left join product_category ctg on ctg.id=a.nhomsp +)m on m.id = n.id +group by name,code +]]> + + + 2.0 + + + + In + + + + 1 + + 4 + 0 + 0 + Unknown + + + + + + In + + + + 2 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 3 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 4 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 5 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 6 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 7 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 8 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 9 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 10 + + 91 + 0 + 0 + Unknown + + + + + + In + + + + 11 + + 91 + 0 + 0 + Unknown + + + + + + + + + + + name + 1 + + 12 + 64 + 0 + NotNullable + + name + + + + name + + 64 + + + + + + + +]]> + + + + + + + + 1 + ngay_vc + date + + + + LAMTHAO + + + 1 + ngay_vc + ngay_vc + date + + + + + + 2.0 + + + + + + + ngay_thanhtoan + 1 + + 91 + 13 + 0 + Nullable + + ngay_thanhtoan + + + + ngay_thanhtoan + + 13 + + + + + + + +]]> + + + + + + + + + + + + + a4 + landscape + 0in + 0.4in + 0in + 0.2in + false + false + 0.1in + 0.1in + + + "Times New Roman" + 11pt + 10.833333333333334in + + 10.229166666666666in + + + 0.2604166666666667in + + + 0.07291666666666667in + + + 0.2708333333333333in + + + + + + + + right + page-number + + + + + center + plain + + + + + + left + total-page + + + + + + + + + + "Times New Roman" + 11pt + bold + black + 0.6875in + 11.020833333333334in + datenow + + + day + day + dataSetRow["day"] + string + + + month + month + dataSetRow["month"] + string + + + year + year + dataSetRow["year"] + string + + + lamthao + "Lâm thao, Ngày "+" Tháng " +" Năm "+dataSetRow["year"] + string + true + + + + 2.6770833333333335in + + + 5.427083333333333in + + + 2.9166666666666665in + + + 0.22916666666666666in + + 0pt + 0pt + 0pt + 0pt + + + + 1 + 1 + 0pt + 0pt + 0pt + 0pt + + + + + + + 0.22916666666666666in + + 0pt + 0pt + 0pt + 0pt + + underline + 0pt + 0pt + 0pt + 0pt + center + info + + + phieu_dn + phieu_dn + "Số: "+dataSetRow["phieu_dn"]+"/KDVT" + string + true + + + phieu_dn + + + + 1 + 1 + 0pt + 0pt + 0pt + 0pt + + + + + 0.22916666666666666in + + + + 0pt + 0pt + 0pt + 0pt + + normal + italic + 0pt + 0pt + 0pt + 0pt + lamthao + + + + + + "Times New Roman" + 11pt + 1.9375in + 11.020833333333334in + + 1.0520833333333333in + + + 1.7291666666666667in + + + 1.03125in + + + 0.8125in + + + 0.7916666666666666in + + + 0.71875in + + + 0.96875in + + + 1.2604166666666667in + + + 1.0416666666666667in + + + 1.6145833333333333in + + + 0.22916666666666666in + + 10 + 1 + 0pt + 0pt + 0pt + 0pt + + + + + 0.22916666666666666in + + + + + 0pt + 0pt + 0pt + 0pt + + + + scroll + url + 0% + 0% + repeat + auto + auto + "Times New Roman" + 11pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0em + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + date + 10pt + bold + 0pt + 0pt + 0pt + 0pt + + Custom + dd-MM-yyyy + vi_VN + + left + + + tu_ngay + params["tu_ngay"] + date + + + tu_ngay + + + + 0pt + 0pt + 0pt + 0pt + + + + scroll + url + 0% + 0% + repeat + auto + auto + "Times New Roman" + 11pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0em + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + date + scroll + 0% + 0% + repeat + "Times New Roman" + 10pt + bold + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Custom + dd-MM-yyyy + vi_VN + + left + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + den_ngay + params["den_ngay"] + date + + + den_ngay + + + + + + + + 0.22916666666666666in + + + 7 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 0pt + 0pt + 0pt + info + + + hopdong + hopdong + var ngay=dataSetRow["day"]; +var thang=dataSetRow["month"] +var ngay_chuoi; +var thang_chuoi; +if (ngay<10) +{ + ngay_chuoi='0'+BirtStr.trim(ngay); +} +else +{ + ngay_chuoi=ngay; +} +if (thang<10) +{ + thang_chuoi='0'+BirtStr.trim(thang); +} +else +{ + thang_chuoi=thang; +} +'Căn cứ hợp đồng sô: '+dataSetRow["hopdong"]+' Ngày '+ ngay_chuoi +' Tháng '+ thang_chuoi +' Năm '+dataSetRow["year"]+',' + string + true + + + hopdong + + + + + + + 0.22916666666666666in + + + 1 + 1 + 0pt + 0pt + 0pt + 0pt + + + + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 0pt + 0pt + 0pt + info + + + phuluc + phuluc + dataSetRow["phuluc"] + string + true + + + phuluc + + + + + + 5 + 1 + 0pt + 0pt + 0pt + 0pt + + + + + 0.22916666666666666in + + + 2 + 1 + 0pt + 0pt + 0pt + 0pt + + + + + + + + + + + + 0.23958333333333334in + + 5 + 1 + 0pt + 0pt + 0pt + 0pt + + + + 5 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 0pt + 0pt + 0pt + info + + + dv_vchuyen + dv_vchuyen + "- ĐẠI DIỆN ĐƠN VỊ VẬN CHUYỂN: "+dataSetRow["dv_vchuyen"] + string + true + + + dv_vchuyen + + + + + 0.21875in + + 2 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 15pt + 0pt + 0pt + info + + + daidien1 + daidien1 + var str = dataSetRow["daidien1"] + if(str == "" || str == null) + { + str = " " + } +"1- Ông/Bà: "+str + string + true + + + daidien1 + + + + 3 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 0pt + 0pt + 0pt + info + + + chucvu1 + chucvu1 + var str = dataSetRow["chucvu1"] + if(str == "" || str == null) + { + str = " " + } +"Chức vụ: "+str + string + true + + + chucvu1 + + + + 3 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 15pt + 0pt + 0pt + info + + + khach1 + khach1 + var str = dataSetRow["khach1"] + if(str == "" || str == null) + { + str = " " + } +"1- Ông/Bà: "+str + string + true + + + khach1 + + + + 2 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 0pt + 0pt + 0pt + info + + + cv_kh1 + cv_kh1 + var str = dataSetRow["cv_kh1"] + if(str == "" || str == null) + { + str = " " + } +"Chức vụ: "+str + string + true + + + cv_kh1 + + + + + 0.22916666666666666in + + 2 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 15pt + 0pt + 0pt + info + + + daidien2 + daidien2 + var str = dataSetRow["daidien2"] + if(str == "" || str == null) + { + str = " " + } +"1- Ông/Bà: "+str + string + true + + + daidien2 + + + + 3 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 0pt + 0pt + 0pt + info + + + chucvu2 + chucvu2 + var str = dataSetRow["chucvu2"] + if(str == "" || str == null) + { + str = " " + } +"Chức vụ: "+str + string + true + + + chucvu2 + + + + 3 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 15pt + 0pt + 0pt + info + + + khach2 + khach2 + var str = dataSetRow["khach2"] + if(str == "" || str == null) + { + str = " " + } +"1- Ông/Bà: "+str + string + true + + + khach2 + + + + 2 + 1 + 0pt + 0pt + 0pt + 0pt + + 10pt + 0pt + 0pt + 0pt + 0pt + info + + + cv_kh2 + cv_kh2 + var str = dataSetRow["cv_kh2"] + if(str == "" || str == null) + { + str = " " + } +"Chức vụ: "+str + string + true + + + cv_kh2 + + + + + + + "Times New Roman" + 11.03125in + ketqua_vanchuyen + + + stt + stt + dataSetRow["stt"] + decimal + + + loai_sp + loai_sp + dataSetRow["loai_sanpham"] + string + true + + + sl_phatsinh + sl_phatsinh + dataSetRow["sl_phatsinh"] + float + + + phieu_phatsinh + phieu_phatsinh + dataSetRow["phieu_phatsinh"] + decimal + + + phieu_thanhtoan + phieu_thanhtoan + dataSetRow["phieu_thanhtoan"] + decimal + + + sl_thanhtoan + sl_thanhtoan + dataSetRow["sl_thanhtoan"] + float + + + phieu_ton + phieu_ton + dataSetRow["phieu_ton"] + decimal + + + sl_ton + sl_ton + dataSetRow["sl_ton"] + float + + + Aggregation + float + SUM + + + Expression + row["sl_phatsinh"] + + + true + + + Aggregation_1 + float + SUM + + + Expression + row["phieu_phatsinh"] + + + true + + + Aggregation_2 + float + SUM + + + Expression + row["sl_thanhtoan"] + + + true + + + Aggregation_3 + float + SUM + + + Expression + row["phieu_thanhtoan"] + + + true + + + Aggregation_4 + float + SUM + + + Expression + row["sl_ton"] + + + true + + + Aggregation_5 + float + SUM + + + Expression + row["phieu_ton"] + + + true + + + + 0.5104166666666666in + + + 2.4375in + + + 1.125in + + + 1.0416666666666667in + + + 1.09375in + + + 1.0520833333333333in + + + 1.09375in + + + 1.125in + + + 1.5520833333333333in + +
+ + header + 0.22916666666666666in + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 2 + 1 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 2 + 1 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 2 + 1 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + + header + 0.22916666666666666in + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + +
+ + + 11pt + right + 0.2604166666666667in + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + 0pt + 0pt + 0pt + 0pt + center + stt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + 0pt + 0pt + 0pt + 0pt + left + loai_sp + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + money + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 1pt + 1pt + 1pt + 1pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + sl_phatsinh + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + money + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Unformatted + vi_VN + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + phieu_phatsinh + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + money + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 1pt + 1pt + 1pt + 1pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + sl_thanhtoan + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + money + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Unformatted + vi_VN + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + phieu_thanhtoan + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + money + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 1pt + 1pt + 1pt + 1pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + sl_ton + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + money + 9pt + 0pt + 0pt + 0pt + 0pt + + Unformatted + vi_VN + + phieu_ton + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + +
+ + + "Times New Roman" + 11pt + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 11.020833333333334in + main + + + stt + stt + dataSetRow["stt"] + decimal + + + dv_nhanhang + dv_nhanhang + dataSetRow["dv_nhanhang"] + string + + + kho_xuat + kho_xuat + dataSetRow["kho_xuat"] + string + + + dc_giao + dc_giao + dataSetRow["dc_giao"] + string + + + npk + npk + dataSetRow["npk"] + float + + + lan + lan + dataSetRow["lan"] + float + + + lannc + lannc + dataSetRow["lannc"] + float + + + khac + khac + dataSetRow["khac"] + float + + + tong + tong + dataSetRow["tong"] + float + + + gia_cuoc + gia_cuoc + dataSetRow["gia_cuoc"] + float + + + thanh_tien + thanh_tien + dataSetRow["thanh_tien"] + float + + + Aggregation + float + SUM + + + Expression + row["thanh_tien"] + + + true + + + Aggregation_1 + float + SUM + + + Expression + row["lan"] + + + true + + + Aggregation_2 + float + SUM + + + Expression + row["npk"] + + + true + + + Aggregation_3 + float + SUM + + + Expression + row["lannc"] + + + true + + + Aggregation_4 + float + SUM + + + Expression + row["axit"] + + + true + + + Aggregation_5 + float + SUM + + + Expression + row["khac"] + + + true + + + Aggregation_6 + float + SUM + + + Expression + row["tong"] + + + true + + + + 0.40625in + + + 1.7291666666666667in + + + 1.0416666666666667in + + + 1.34375in + + + 0.6770833333333334in + + + 0.5833333333333334in + + + 0.6145833333333334in + + + 0.6979166666666666in + + + 0.5833333333333334in + + + 0.8229166666666666in + + + 1.3125in + + + 1.1041666666666667in + +
+ + header + 0.22916666666666666in + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 5 + 1 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 1 + 2 + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + 1 + 2 + scroll + 0% + 0% + repeat + "Times New Roman" + 11pt + bold + normal + normal + black + none + none + none + #000000 + solid + 1px + #000000 + solid + 1px + #000000 + solid + 1px + #000000 + solid + 1px + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + + 1 + 2 + scroll + 0% + 0% + repeat + "Times New Roman" + 11pt + bold + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + center + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + + + + + header + 0.22916666666666666in + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + + + +
+ + + 9pt + 0.23958333333333334in + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + 0pt + 0pt + 0pt + 0pt + center + stt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + 0pt + 0pt + 0pt + 0pt + dv_nhanhang + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + 0pt + 0pt + 0pt + 0pt + kho_xuat + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + 0pt + 0pt + 0pt + 0pt + dc_giao + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + lan + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + lannc + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + npk + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + khac + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Custom + ##0,# + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + tong + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Currency + #,##0{RoundingMode=HALF_UP} + vi_VN + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + gia_cuoc + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + 0pt + + Currency + #,##0{RoundingMode=HALF_UP} + vi_VN + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + thanh_tien + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + + + + +
+ + + "Times New Roman" + 11pt + 11.041666666666666in + phieu_vipham + + + stt + stt + dataSetRow["stt"] + decimal + + + dv_nhan + dv_nhan + dataSetRow["dv_nhan"] + string + + + sl_haohut + sl_haohut + dataSetRow["sl_haohut"] + float + + + loai_sp + loai_sp + dataSetRow["loai_sp"] + string + true + + + don_gia + don_gia + dataSetRow["don_gia"] + float + + + name + name + dataSetRow["name"] + string + + + thanh_tien + thanh_tien + dataSetRow["thanh_tien"] + float + + + tong_sl + float + SUM + + + Expression + row["sl_haohut"] + + + true + + + tong_tien + float + SUM + + + Expression + row["thanh_tien"] + + + true + + + + 0.46875in + + + 3.1354166666666665in + + + 1.0104166666666667in + + + 2.1979166666666665in + + + 0.8645833333333334in + + + 1.21875in + + + 2.1458333333333335in + +
+ + header + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0.22916666666666666in + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + +
+ + NewTableGroup1 + row["stt"] + + row["stt"] + + false + + + + 10pt + 0.25in + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + center + stt + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + dv_nhan + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + + Fixed + ###0.00{RoundingMode=HALF_UP} + vi + + right + sl_haohut + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + 3pt + loai_sp + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + 9pt + + Currency + #,##0{RoundingMode=HALF_UP} + vi + + right + don_gia + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + normal + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 1pt + 1pt + 1pt + 1pt + + Currency + #,##0{RoundingMode=HALF_UP} + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + thanh_tien + + + + solid + 1px + solid + 1px + solid + 1px + solid + 1px + 0pt + 0pt + 0pt + 0pt + + + + +
+ + "Times New Roman" + 11pt + bold + center + 1in + 11.0625in + main + + + stt + stt + dataSetRow["stt"] + decimal + + + dv_nhanhang + dv_nhanhang + dataSetRow["dv_nhanhang"] + string + + + dc_giao + dc_giao + dataSetRow["dc_giao"] + string + + + kho_xuat + kho_xuat + dataSetRow["kho_xuat"] + string + + + npk + npk + dataSetRow["npk"] + float + + + lan + lan + dataSetRow["lan"] + float + + + khac + khac + dataSetRow["khac"] + float + + + lannc + lannc + dataSetRow["lannc"] + float + + + tong + tong + dataSetRow["tong"] + float + + + thanh_tien + thanh_tien + dataSetRow["thanh_tien"] + float + + + gia_cuoc + gia_cuoc + dataSetRow["gia_cuoc"] + float + + + kho_xuat_12 + kho_xuat + dataSetRow["kho_xuat_12"] + string + + + tongtien + tongtien + float + SUM + + + Expression + row["thanh_tien"] + + + true + + + docso + var th = ['','ngàn','triệu', 'tỉ','ngàn tỉ']; +var dg = ['không','một','hai','ba','bốn', 'năm','sáu','bảy','tám','chín']; + var tn = ['mười','mười một','mười hai','mười ba', 'mười bốn','mười lăm','mười sáu', 'mười bảy','mười tám','mười chín']; + var tw = ['hai mươi','ba mươi','bốn mươi','năm mươi', 'sáu mươi','bảy mươi','tám mươi','chín mươi']; + function toWords(s){s = s.toString(); s = s.replace(/[\, ]/g,''); + if (s != parseFloat(s)) + return 'Không phải số'; +var x = s.indexOf('.'); +if (x == -1) x = s.length; +if (x > 15) + return 'Số quá lớn'; + var n = s.split(''); var str = ''; var sk = 0; for (var i=0; i < x; i++) {if ((x-i)%3==2) {if (n[i] == '1') {str += tn[Number(n[i+1])] + ' '; i++; sk=1;} else if (n[i]!=0) {str += tw[n[i]-2] + ' ';sk=1;}} else if (n[i]!=0) {str += dg[n[i]] +' '; if ((x-i)%3==0) str += 'trăm ';sk=1;} if ((x-i)%3==1) {if (sk) str += th[(x-i-1)/3] + ' ';sk=0;}} if (x != s.length) {var y = s.length; str += 'phẩy '; for (var i=x+1; i<y; i++) str += dg[n[i]] +' ';} return str.replace(/\s+/g,' ');} +var stringss=toWords(row["tongtien"])+ 'đồng'; +stringss.charAt(0).toUpperCase()+stringss.slice(1) + string + true + + + + + + 1.7604166666666667in + + + 1.7604166666666667in + + + 2.9270833333333335in + + + 0.25in + + 0pt + 0pt + 0pt + 0pt + + + + 1 + 1 + 0pt + 0pt + 0pt + 0pt + left + + scroll + 0% + 0% + repeat + "Times New Roman" + 9pt + bold + normal + normal + black + none + none + none + black + none + medium + black + none + medium + black + none + medium + black + none + medium + 0pt + 0pt + 0pt + 0pt + 1pt + 1pt + 1pt + 1pt + + Currency + #,##0{RoundingMode=HALF_UP} + vi + + right + normal + normal + 2 + none + normal + 2 + normal + block + auto + auto + auto + false + false + hidden + tongtien + + + + 0pt + 0pt + 0pt + 0pt + left + + + + 0pt + 0pt + 0pt + 0pt + left + + + 0pt + 0pt + 0pt + 0pt + left + + + + 0.25in + + 0pt + 0pt + 0pt + 0pt + + + + 4 + 1 + 0pt + 0pt + 0pt + 0pt + + 9pt + -5pt + left + docso + + + + + 0.23958333333333334in + + 0pt + 0pt + 0pt + 0pt + + + + 0pt + 0pt + 0pt + 0pt + + + + 2 + 1 + 0pt + 0pt + 0pt + 0pt + + + + 0pt + 0pt + 0pt + 0pt + + + + + 0.2708333333333333in + + 1 + 1 + 0pt + 0pt + 0pt + 0pt + + + 0pt + 0pt + 0pt + 0pt + + + 1 + 1 + 0pt + 0pt + 0pt + 0pt + + + + 0pt + 0pt + 0pt + 0pt + + + + 0pt + 0pt + 0pt + 0pt + + + + +
diff --git a/addons/__init__.py b/addons/__init__.py new file mode 100644 index 0000000000000..9554e9bd85092 --- /dev/null +++ b/addons/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# Copyright (C) 2010-2011 OpenERP s.a. (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +""" Addons module. + +This module serves to contain all OpenERP addons, across all configured addons +paths. For the code to manage those addons, see openerp.modules. + +Addons are made available under `openerp.addons` after +openerp.tools.config.parse_config() is called (so that the addons paths are +known). + +This module also conveniently reexports some symbols from openerp.modules. +Importing them from here is deprecated. + +""" + +# get_module_path is used only by base_module_quality +from openerp.modules import get_module_resource, get_module_path + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index d236d2643b810..43aaa2f58c131 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -153,12 +153,12 @@ 'test/account_period_close.yml', 'test/account_use_model.yml', 'test/account_validate_account_move.yml', + 'test/account_fiscalyear_close.yml', #'test/account_bank_statement.yml', #'test/account_cash_statement.yml', 'test/test_edi_invoice.yml', 'test/account_report.yml', - 'test/analytic_hierarchy.yml', - 'test/account_fiscalyear_close.yml', #last test, as it will definitively close the demo fiscalyear + 'test/account_fiscalyear_close_state.yml', #last test, as it will definitively close the demo fiscalyear ], 'installable': True, 'auto_install': False, diff --git a/addons/account/account.py b/addons/account/account.py index d64c9ec3826a6..fc5d043c17387 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -25,13 +25,11 @@ from operator import itemgetter import time -import openerp from openerp import SUPERUSER_ID from openerp import pooler, tools -from openerp.osv import fields, osv, expression +from openerp.osv import fields, osv from openerp.tools.translate import _ -from openerp.tools.float_utils import float_round as round -from openerp.tools.safe_eval import safe_eval as eval +from openerp.tools.float_utils import float_round import openerp.addons.decimal_precision as dp @@ -227,37 +225,30 @@ class account_account(osv.osv): _description = "Account" _parent_store = True - def _where_calc(self, cr, uid, domain, active_test=True, context=None): - """ Convert domains to allow easier filtering: - code: force case insensitive and right side matching search - journal_id: restrict to the accounts sharing the same account.account.type - """ + def search(self, cr, uid, args, offset=0, limit=None, order=None, + context=None, count=False): + if context is None: + context = {} pos = 0 - while pos < len(domain): - if domain[pos][0] == 'code' and domain[pos][1] in ('like', 'ilike') and domain[pos][2]: - domain[pos] = ('code', '=like', tools.ustr(domain[pos][2].replace('%', '')) + '%') - if domain[pos][0] == 'journal_id': - if not domain[pos][2]: - del domain[pos] + + while pos < len(args): + + if args[pos][0] == 'code' and args[pos][1] in ('like', 'ilike') and args[pos][2]: + args[pos] = ('code', '=like', tools.ustr(args[pos][2].replace('%', ''))+'%') + if args[pos][0] == 'journal_id': + if not args[pos][2]: + del args[pos] continue - jour = self.pool.get('account.journal').browse(cr, uid, domain[pos][2], context=context) - if (not (jour.account_control_ids or jour.type_control_ids)) or not domain[pos][2]: - domain[pos] = ('type', 'not in', ('consolidation', 'view')) + jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2], context=context) + if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]: + args[pos] = ('type','not in',('consolidation','view')) continue ids3 = map(lambda x: x.id, jour.type_control_ids) ids1 = super(account_account, self).search(cr, uid, [('user_type', 'in', ids3)]) ids1 += map(lambda x: x.id, jour.account_control_ids) - domain[pos] = ('id', 'in', ids1) + args[pos] = ('id', 'in', ids1) pos += 1 - return super(account_account, self)._where_calc(cr, uid, domain, active_test, context) - - def search(self, cr, uid, args, offset=0, limit=None, order=None, - context=None, count=False): - """ Check presence of key 'consolidate_children' in context to include also the Consolidated Children - of found accounts into the result of the search - """ - if context and context.has_key('consolidate_children'): #add consolidated children of accounts ids = super(account_account, self).search(cr, uid, args, offset, limit, order, context=context, count=count) @@ -590,18 +581,15 @@ def name_search(self, cr, user, name, args=None, operator='ilike', context=None, except: pass if name: - if operator not in expression.NEGATIVE_TERM_OPERATORS: - ids = self.search(cr, user, ['|', ('code', '=like', name+"%"), '|', ('shortcut', '=', name), ('name', operator, name)]+args, limit=limit) - if not ids and len(name.split()) >= 2: - #Separating code and name of account for searching - operand1,operand2 = name.split(' ',1) #name can contain spaces e.g. OpenERP S.A. - ids = self.search(cr, user, [('code', operator, operand1), ('name', operator, operand2)]+ args, limit=limit) - else: - ids = self.search(cr, user, ['&','!', ('code', '=like', name+"%"), ('name', operator, name)]+args, limit=limit) - # as negation want to restric, do if already have results - if ids and len(name.split()) >= 2: - operand1,operand2 = name.split(' ',1) #name can contain spaces e.g. OpenERP S.A. - ids = self.search(cr, user, [('code', operator, operand1), ('name', operator, operand2), ('id', 'in', ids)]+ args, limit=limit) + ids = self.search(cr, user, [('code', '=like', name+"%")]+args, limit=limit) + if not ids: + ids = self.search(cr, user, [('shortcut', '=', name)]+ args, limit=limit) + if not ids: + ids = self.search(cr, user, [('name', operator, name)]+ args, limit=limit) + if not ids and len(name.split()) >= 2: + #Separating code and name of account for searching + operand1,operand2 = name.split(' ',1) #name can contain spaces e.g. OpenERP S.A. + ids = self.search(cr, user, [('code', operator, operand1), ('name', operator, operand2)]+ args, limit=limit) else: ids = self.search(cr, user, args, context=context, limit=limit) return self.name_get(cr, user, ids, context=context) @@ -853,11 +841,16 @@ def name_get(self, cr, user, ids, context=None): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100): if not args: args = [] - if operator in expression.NEGATIVE_TERM_OPERATORS: - domain = [('code', operator, name), ('name', operator, name)] - else: - domain = ['|', ('code', operator, name), ('name', operator, name)] - ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) + if context is None: + context = {} + ids = [] + if context.get('journal_type', False): + args += [('type','=',context.get('journal_type'))] + if name: + ids = self.search(cr, user, [('code', 'ilike', name)]+ args, limit=limit, context=context) + if not ids: + ids = self.search(cr, user, [('name', 'ilike', name)]+ args, limit=limit, context=context)#fix it ilike should be replace with operator + return self.name_get(cr, user, ids, context=context) account_journal() @@ -947,11 +940,13 @@ def finds(self, cr, uid, dt=None, exception=True, context=None): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): if args is None: args = [] - if operator in expression.NEGATIVE_TERM_OPERATORS: - domain = [('code', operator, name), ('name', operator, name)] - else: - domain = ['|', ('code', operator, name), ('name', operator, name)] - ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) + if context is None: + context = {} + ids = [] + if name: + ids = self.search(cr, user, [('code', 'ilike', name)]+ args, limit=limit) + if not ids: + ids = self.search(cr, user, [('name', operator, name)]+ args, limit=limit) return self.name_get(cr, user, ids, context=context) account_fiscalyear() @@ -1046,11 +1041,19 @@ def action_draft(self, cr, uid, ids, *args): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100): if args is None: args = [] - if operator in expression.NEGATIVE_TERM_OPERATORS: - domain = [('code', operator, name), ('name', operator, name)] - else: - domain = ['|', ('code', operator, name), ('name', operator, name)] - ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) + if context is None: + context = {} + ids = [] + if name: + ids = self.search(cr, user, + [('code', 'ilike', name)] + args, + limit=limit, + context=context) + if not ids: + ids = self.search(cr, user, + [('name', operator, name)] + args, + limit=limit, + context=context) return self.name_get(cr, user, ids, context=context) def write(self, cr, uid, ids, vals, context=None): @@ -1165,19 +1168,6 @@ class account_move(osv.osv): _description = "Account Entry" _order = 'id desc' - def account_assert_balanced(self, cr, uid, context=None): - cr.execute("""\ - SELECT move_id - FROM account_move_line - WHERE state = 'valid' - GROUP BY move_id - HAVING abs(sum(debit) - sum(credit)) > 0.00001 - """) - assert len(cr.fetchall()) == 0, \ - "For all Journal Items, the state is valid implies that the sum " \ - "of credits equals the sum of debits" - return True - def account_move_prepare(self, cr, uid, journal_id, date=False, ref='', company_id=False, context=None): ''' Prepares and returns a dictionary of values, ready to be passed to create() based on the parameters received. @@ -1201,6 +1191,36 @@ def account_move_prepare(self, cr, uid, journal_id, date=False, ref='', company_ 'company_id': company_id, } + def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): + """ + Returns a list of tupples containing id, name, as internally it is called {def name_get} + result format: {[(id, name), (id, name), ...]} + + @param cr: A database cursor + @param user: ID of the user currently logged in + @param name: name to search + @param args: other arguments + @param operator: default operator is 'ilike', it can be changed + @param context: context arguments, like lang, time zone + @param limit: Returns first 'n' ids of complete result, default is 80. + + @return: Returns a list of tuples containing id and name + """ + + if not args: + args = [] + ids = [] + if name: + ids += self.search(cr, user, [('name','ilike',name)]+args, limit=limit, context=context) + + if not ids and name and type(name) == int: + ids += self.search(cr, user, [('id','=',name)]+args, limit=limit, context=context) + + if not ids: + ids += self.search(cr, user, args, limit=limit, context=context) + + return self.name_get(cr, user, ids, context=context) + def name_get(self, cursor, user, ids, context=None): if isinstance(ids, (int, long)): ids = [ids] @@ -1405,17 +1425,14 @@ def create(self, cr, uid, vals, context=None): l[2]['period_id'] = default_period context['period_id'] = default_period - if vals.get('line_id', False): + if 'line_id' in vals: c = context.copy() c['novalidate'] = True c['period_id'] = vals['period_id'] if 'period_id' in vals else self._get_period(cr, uid, context) c['journal_id'] = vals['journal_id'] if 'date' in vals: c['date'] = vals['date'] result = super(account_move, self).create(cr, uid, vals, c) - tmp = self.validate(cr, uid, [result], context) - journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'], context) - if journal.entry_posted and tmp: - self.button_validate(cr,uid, [result], context) + self.validate(cr, uid, [result], context) else: result = super(account_move, self).create(cr, uid, vals, context) return result @@ -1436,8 +1453,6 @@ def copy(self, cr, uid, id, default=None, context=None): def unlink(self, cr, uid, ids, context=None, check=True): if context is None: context = {} - if isinstance(ids, (int, long)): - ids = [ids] toremove = [] obj_move_line = self.pool.get('account.move.line') for move in self.browse(cr, uid, ids, context=context): @@ -1562,6 +1577,11 @@ def validate(self, cr, uid, ids, context=None): obj_analytic_line = self.pool.get('account.analytic.line') obj_move_line = self.pool.get('account.move.line') for move in self.browse(cr, uid, ids, context): + # Unlink old analytic lines on move_lines + for obj_line in move.line_id: + for obj in obj_line.analytic_lines: + obj_analytic_line.unlink(cr,uid,obj.id) + journal = move.journal_id amount = 0 line_ids = [] @@ -1580,7 +1600,7 @@ def validate(self, cr, uid, ids, context=None): if line.account_id.currency_id and line.currency_id: if line.account_id.currency_id.id != line.currency_id.id and (line.account_id.currency_id.id != line.account_id.company_id.currency_id.id): - raise osv.except_osv(_('Error'), _("""Couldn't create move with currency different from the secondary currency of the account "%s - %s". Clear the secondary currency field of the account definition if you want to accept all currencies.""") % (line.account_id.code, line.account_id.name)) + raise osv.except_osv(_('Error!'), _("""Cannot create move with currency different from ..""") % (line.account_id.code, line.account_id.name)) if abs(amount) < 10 ** -4: # If the move is balanced @@ -1703,23 +1723,22 @@ def reconcile_partial_check(self, cr, uid, ids, type='auto', context=None): if not total: self.pool.get('account.move.line').write(cr, uid, map(lambda x: x.id, rec.line_partial_ids), - {'reconcile_id': rec.id }, - context=context + {'reconcile_id': rec.id } ) return True - def name_get(self, cr, uid, ids, context=None): - if not ids: - return [] - result = [] - for r in self.browse(cr, uid, ids, context=context): - total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0) - if total: - name = '%s (%.2f)' % (r.name, total) - result.append((r.id,name)) - else: - result.append((r.id,r.name)) - return result +# def name_get(self, cr, uid, ids, context=None): +# if not ids: +# return [] +# result = [] +# for r in self.browse(cr, uid, ids, context=context): +# total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0) +# if total: +# name = '%s (%.2f)' % (r.name, total) +# result.append((r.id,name)) +# else: +# result.append((r.id,r.name)) +# return result account_move_reconcile() @@ -1763,7 +1782,7 @@ def _sum(self, cr, uid, ids, name, args, context, where ='', where_params=()): res2 = {} for record in self.browse(cr, uid, ids, context=context): def _rec_get(record): - amount = res.get(record.id) or 0.0 + amount = res.get(record.id, 0.0) for rec in record.child_ids: amount += _rec_get(rec) * rec.sign return amount @@ -1830,12 +1849,10 @@ def _sum_period(self, cr, uid, ids, name, args, context): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): if not args: args = [] - if operator in expression.NEGATIVE_TERM_OPERATORS: - domain = [('code', operator, name), ('name', operator, name)] - else: - domain = ['|', ('code', operator, name), ('name', operator, name)] - ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) - return self.name_get(cr, user, ids, context=context) + if context is None: + context = {} + ids = self.search(cr, user, ['|',('name',operator,name),('code',operator,name)] + args, limit=limit, context=context) + return self.name_get(cr, user, ids, context) def name_get(self, cr, uid, ids, context=None): if isinstance(ids, (int, long)): @@ -1928,15 +1945,15 @@ def copy_data(self, cr, uid, id, default=None, context=None): # 'base_code_id': fields.many2one('account.tax.code', 'Account Base Code', help="Use this code for the tax declaration."), 'tax_code_id': fields.many2one('account.tax.code', 'Account Tax Code', help="Use this code for the tax declaration."), - 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), - 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), + 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), + 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), # Same fields for refund invoices 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the tax declaration."), 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the tax declaration."), - 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), - 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), + 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), + 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), 'include_base_amount': fields.boolean('Included in base amount', help="Indicates if the amount of tax must be included in the base amount for the computation of the next taxes"), 'company_id': fields.many2one('res.company', 'Company', required=True), 'description': fields.char('Tax Code'), @@ -1965,11 +1982,15 @@ def name_search(self, cr, user, name, args=None, operator='ilike', context=None, """ if not args: args = [] - if operator in expression.NEGATIVE_TERM_OPERATORS: - domain = [('description', operator, name), ('name', operator, name)] + if context is None: + context = {} + ids = [] + if name: + ids = self.search(cr, user, [('description', '=', name)] + args, limit=limit, context=context) + if not ids: + ids = self.search(cr, user, [('name', operator, name)] + args, limit=limit, context=context) else: - domain = ['|', ('description', operator, name), ('name', operator, name)] - ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) + ids = self.search(cr, user, args, limit=limit, context=context or {}) return self.name_get(cr, user, ids, context=context) def write(self, cr, uid, ids, vals, context=None): @@ -1978,17 +1999,15 @@ def write(self, cr, uid, ids, vals, context=None): return super(account_tax, self).write(cr, uid, ids, vals, context=context) def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): - if context is None: - context = {} journal_pool = self.pool.get('account.journal') - if context.get('type'): + if context and context.has_key('type'): if context.get('type') in ('out_invoice','out_refund'): args += [('type_tax_use','in',['sale','all'])] elif context.get('type') in ('in_invoice','in_refund'): args += [('type_tax_use','in',['purchase','all'])] - if context.get('journal_id'): + if context and context.has_key('journal_id'): journal = journal_pool.browse(cr, uid, context.get('journal_id')) if journal.type in ('sale', 'purchase'): args += [('type_tax_use','in',[journal.type,'all'])] @@ -2034,7 +2053,7 @@ def _applicable(self, cr, uid, taxes, price_unit, product=None, partner=None): for tax in taxes: if tax.applicable_type=='code': localdict = {'price_unit':price_unit, 'product':product, 'partner':partner} - eval(tax.python_applicable, localdict, mode="exec", nocopy=True) + exec tax.python_applicable in localdict if localdict.get('result', False): res.append(tax) else: @@ -2075,7 +2094,7 @@ def _unit_compute(self, cr, uid, taxes, price_unit, product=None, partner=None, # data['amount'] = quantity elif tax.type=='code': localdict = {'price_unit':cur_price_unit, 'product':product, 'partner':partner} - eval(tax.python_compute, localdict, mode="exec", nocopy=True) + exec tax.python_compute in localdict amount = localdict['result'] data['amount'] = amount elif tax.type=='balance': @@ -2089,8 +2108,6 @@ def _unit_compute(self, cr, uid, taxes, price_unit, product=None, partner=None, amount = amount2 child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, product, partner, quantity) res.extend(child_tax) - for child in child_tax: - amount2 += child.get('amount', 0.0) if tax.child_depend: for r in res: for name in ('base','ref_base'): @@ -2134,7 +2151,7 @@ def compute_all(self, cr, uid, taxes, price_unit, quantity, product=None, partne tax_compute_precision = precision if taxes and taxes[0].company_id.tax_calculation_rounding_method == 'round_globally': tax_compute_precision += 5 - totalin = totalex = round(price_unit * quantity, precision) + totalin = totalex = float_round(price_unit * quantity, precision) tin = [] tex = [] for tax in taxes: @@ -2211,7 +2228,7 @@ def _unit_compute_inv(self, cr, uid, taxes, price_unit, product=None, partner=No elif tax.type=='code': localdict = {'price_unit':cur_price_unit, 'product':product, 'partner':partner} - eval(tax.python_compute_inv, localdict, mode="exec", nocopy=True) + exec tax.python_compute_inv in localdict amount = localdict['result'] elif tax.type=='balance': amount = cur_price_unit - reduce(lambda x,y: y.get('amount',0.0)+x, res, 0.0) @@ -2957,7 +2974,7 @@ class account_fiscal_position_template(osv.osv): 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True), 'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Account Mapping'), 'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Tax Mapping'), - 'note': fields.text('Notes'), + 'note': fields.text('Notes', translate=True), } def generate_fiscal_position(self, cr, uid, chart_temp_id, tax_template_ref, acc_template_ref, company_id, context=None): @@ -3053,19 +3070,11 @@ class wizard_multi_charts_accounts(osv.osv_memory): 'complete_tax_set': fields.boolean('Complete Set of Taxes', help='This boolean helps you to choose if you want to propose to the user to encode the sales and purchase rates or use the usual m2o fields. This last choice assumes that the set of tax defined for the chosen template is complete'), } - - def _get_chart_parent_ids(self, cr, uid, chart_template, context=None): - """ Returns the IDs of all ancestor charts, including the chart itself. - (inverse of child_of operator) - - :param browse_record chart_template: the account.chart.template record - :return: the IDS of all ancestor charts, including the chart itself. - """ - result = [chart_template.id] - while chart_template.parent_id: - chart_template = chart_template.parent_id - result.append(chart_template.id) - return result + def onchange_company_id(self, cr, uid, ids, company_id, context=None): + currency_id = False + if company_id: + currency_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id.id + return {'value': {'currency_id': currency_id}} def onchange_tax_rate(self, cr, uid, ids, rate=False, context=None): return {'value': {'purchase_tax_rate': rate or False}} @@ -3073,30 +3082,18 @@ def onchange_tax_rate(self, cr, uid, ids, rate=False, context=None): def onchange_chart_template_id(self, cr, uid, ids, chart_template_id=False, context=None): res = {} tax_templ_obj = self.pool.get('account.tax.template') - ir_values = self.pool.get('ir.values') res['value'] = {'complete_tax_set': False, 'sale_tax': False, 'purchase_tax': False} if chart_template_id: data = self.pool.get('account.chart.template').browse(cr, uid, chart_template_id, context=context) - #set currecy_id based on selected COA template using ir.vaalues else current users company's currency - value_id = ir_values.search(cr, uid, [('model', '=', 'account.chart.template'), ('res_id', '=', chart_template_id)], limit=1, context=context) - if value_id: - currency_id = int(ir_values.browse(cr, uid, value_id[0], context=context).value) - else: - currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id - res['value'].update({'complete_tax_set': data.complete_tax_set, 'currency_id': currency_id}) + res['value'].update({'complete_tax_set': data.complete_tax_set}) if data.complete_tax_set: # default tax is given by the lowest sequence. For same sequence we will take the latest created as it will be the case for tax created while isntalling the generic chart of account - chart_ids = self._get_chart_parent_ids(cr, uid, data, context=context) - base_tax_domain = [("chart_template_id", "in", chart_ids), ('parent_id', '=', False)] - sale_tax_domain = base_tax_domain + [('type_tax_use', 'in', ('sale','all'))] - purchase_tax_domain = base_tax_domain + [('type_tax_use', 'in', ('purchase','all'))] - sale_tax_ids = tax_templ_obj.search(cr, uid, sale_tax_domain, order="sequence, id desc") - purchase_tax_ids = tax_templ_obj.search(cr, uid, purchase_tax_domain, order="sequence, id desc") - res['value'].update({'sale_tax': sale_tax_ids and sale_tax_ids[0] or False, - 'purchase_tax': purchase_tax_ids and purchase_tax_ids[0] or False}) - res.setdefault('domain', {}) - res['domain']['sale_tax'] = repr(sale_tax_domain) - res['domain']['purchase_tax'] = repr(purchase_tax_domain) + sale_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" + , "=", chart_template_id), ('type_tax_use', 'in', ('sale','all'))], order="sequence, id desc") + purchase_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" + , "=", chart_template_id), ('type_tax_use', 'in', ('purchase','all'))], order="sequence, id desc") + res['value'].update({'sale_tax': sale_tax_ids and sale_tax_ids[0] or False, 'purchase_tax': purchase_tax_ids and purchase_tax_ids[0] or False}) + if data.code_digits: res['value'].update({'code_digits': data.code_digits}) return res @@ -3104,8 +3101,6 @@ def onchange_chart_template_id(self, cr, uid, ids, chart_template_id=False, cont def default_get(self, cr, uid, fields, context=None): res = super(wizard_multi_charts_accounts, self).default_get(cr, uid, fields, context=context) tax_templ_obj = self.pool.get('account.tax.template') - account_chart_template = self.pool['account.chart.template'] - data_obj = self.pool.get('ir.model.data') if 'bank_accounts_id' in fields: res.update({'bank_accounts_id': [{'acc_name': _('Cash'), 'account_type': 'cash'},{'acc_name': _('Bank'), 'account_type': 'bank'}]}) @@ -3119,28 +3114,17 @@ def default_get(self, cr, uid, fields, context=None): currency_id = company_obj.on_change_country(cr, uid, company_id, country_id, context=context)['value']['currency_id'] res.update({'currency_id': currency_id}) - ids = account_chart_template.search(cr, uid, [('visible', '=', True)], context=context) + ids = self.pool.get('account.chart.template').search(cr, uid, [('visible', '=', True)], context=context) if ids: - #in order to set default chart which was last created set max of ids. - chart_id = max(ids) - if context.get("default_charts"): - data_ids = data_obj.search(cr, uid, [('model', '=', 'account.chart.template'), ('module', '=', context.get("default_charts"))], limit=1, context=context) - if data_ids: - chart_id = data_obj.browse(cr, uid, data_ids[0], context=context).res_id - chart = account_chart_template.browse(cr, uid, chart_id, context=context) - chart_hierarchy_ids = self._get_chart_parent_ids(cr, uid, chart, context=context) if 'chart_template_id' in fields: - res.update({'only_one_chart_template': len(ids) == 1, - 'chart_template_id': chart_id}) + res.update({'only_one_chart_template': len(ids) == 1, 'chart_template_id': ids[0]}) if 'sale_tax' in fields: - sale_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id", "in", chart_hierarchy_ids), - ('type_tax_use', 'in', ('sale','all'))], - order="sequence") + sale_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" + , "=", ids[0]), ('type_tax_use', 'in', ('sale','all'))], order="sequence") res.update({'sale_tax': sale_tax_ids and sale_tax_ids[0] or False}) if 'purchase_tax' in fields: - purchase_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id", "in", chart_hierarchy_ids), - ('type_tax_use', 'in', ('purchase','all'))], - order="sequence") + purchase_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" + , "=", ids[0]), ('type_tax_use', 'in', ('purchase','all'))], order="sequence") res.update({'purchase_tax': purchase_tax_ids and purchase_tax_ids[0] or False}) res.update({ 'purchase_tax_rate': 15.0, @@ -3408,7 +3392,12 @@ def _create_tax_templates_from_rates(self, cr, uid, obj_wizard, company_id, cont obj_tax_temp = self.pool.get('account.tax.template') chart_template = obj_wizard.chart_template_id vals = {} - all_parents = self._get_chart_parent_ids(cr, uid, chart_template, context=context) + # get the ids of all the parents of the selected account chart template + current_chart_template = chart_template + all_parents = [current_chart_template.id] + while current_chart_template.parent_id: + current_chart_template = current_chart_template.parent_id + all_parents.append(current_chart_template.id) # create tax templates and tax code templates from purchase_tax_rate and sale_tax_rate fields if not chart_template.complete_tax_set: value = obj_wizard.sale_tax_rate @@ -3425,8 +3414,6 @@ def execute(self, cr, uid, ids, context=None): all the provided information to create the accounts, the banks, the journals, the taxes, the tax codes, the accounting properties... accordingly for the chosen company. ''' - if uid != SUPERUSER_ID and not self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager'): - raise openerp.exceptions.AccessError(_("Only administrators can change the settings")) obj_data = self.pool.get('ir.model.data') ir_values_obj = self.pool.get('ir.values') obj_wizard = self.browse(cr, uid, ids[0]) @@ -3443,7 +3430,7 @@ def execute(self, cr, uid, ids, context=None): self.pool.get(tmp2[0]).write(cr, uid, tmp2[1], { 'currency_id': obj_wizard.currency_id.id }) - except ValueError: + except ValueError, e: pass # If the floats for sale/purchase rates have been filled, create templates from them diff --git a/addons/account/account_assert_test.xml b/addons/account/account_assert_test.xml index 32e8afb1b5002..88025514763b7 100644 --- a/addons/account/account_assert_test.xml +++ b/addons/account/account_assert_test.xml @@ -1,6 +1,8 @@ - + + + diff --git a/addons/account/account_installer.xml b/addons/account/account_installer.xml index 8d1b25b299ffc..b03babc63aca4 100644 --- a/addons/account/account_installer.xml +++ b/addons/account/account_installer.xml @@ -10,7 +10,7 @@
-
diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 6aa17a24c0a2c..1c4f050463871 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -11,7 +11,6 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License @@ -23,12 +22,12 @@ from lxml import etree import openerp.addons.decimal_precision as dp import openerp.exceptions - -from openerp import netsvc, SUPERUSER_ID +import logging +from openerp import netsvc from openerp import pooler from openerp.osv import fields, osv, orm -from openerp.tools import float_compare from openerp.tools.translate import _ +_logger = logging.getLogger(__name__) class account_invoice(osv.osv): def _amount_all(self, cr, uid, ids, name, args, context=None): @@ -37,13 +36,37 @@ def _amount_all(self, cr, uid, ids, name, args, context=None): res[invoice.id] = { 'amount_untaxed': 0.0, 'amount_tax': 0.0, - 'amount_total': 0.0 + 'amount_total': 0.0, + 'phi_baove_moitruong': 0.0, + 'tien_hang': 0.0, + 'chiet_khau': 0.0 } + for line in invoice.invoice_line: res[invoice.id]['amount_untaxed'] += line.price_subtotal + string_code= line.product_id.categ_id.code + tax_line_amount=0 + for line_tax in line.invoice_line_tax_id: + tax_line_amount=line_tax.amount + if string_code: + code=string_code[:2] + if code!='CP': + tien=line.price_subtotal+line.price_subtotal*tax_line_amount + res[invoice.id]['tien_hang'] += tien + else: + chietkhau= (line.price_subtotal + line.price_subtotal*tax_line_amount) + res[invoice.id]['chiet_khau'] +=chietkhau + else: + res[invoice.id]['tien_hang'] += (line.price_subtotal+line.price_subtotal*tax_line_amount) for line in invoice.tax_line: - res[invoice.id]['amount_tax'] += line.amount - res[invoice.id]['amount_total'] = res[invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed'] + res[invoice.id]['amount_tax'] += round(line.amount,0) + res[invoice.id]['phi_baove_moitruong'] = round(res[invoice.id]['amount_untaxed'],0)*0.1 + if invoice.co_phi_baove: + res[invoice.id]['amount_total'] = round(res[invoice.id]['amount_tax'] ,0)+ res[invoice.id]['amount_untaxed']+res[invoice.id]['phi_baove_moitruong'] + + else: + res[invoice.id]['amount_total'] = round(res[invoice.id]['amount_tax'],0) + res[invoice.id]['amount_untaxed'] + return res def _get_journal(self, cr, uid, context=None): @@ -102,7 +125,7 @@ def _amount_residual(self, cr, uid, ids, name, args, context=None): ctx = context.copy() result = {} currency_obj = self.pool.get('res.currency') - for invoice in self.browse(cr, SUPERUSER_ID, ids, context=context): + for invoice in self.browse(cr, uid, ids, context=context): nb_inv_in_partial_rec = max_invoice_id = 0 result[invoice.id] = 0.0 if invoice.move_id: @@ -118,7 +141,7 @@ def _amount_residual(self, cr, uid, ids, name, args, context=None): #we check if the invoice is partially reconciled and if there are other invoices #involved in this partial reconciliation (and we sum these invoices) for line in aml.reconcile_partial_id.line_partial_ids: - if line.invoice and invoice.type == line.invoice.type: + if line.invoice: nb_inv_in_partial_rec += 1 #store the max invoice id as for this invoice we will make a balance instead of a simple division max_invoice_id = max(max_invoice_id, line.invoice.id) @@ -134,7 +157,7 @@ def _amount_residual(self, cr, uid, ids, name, args, context=None): result[invoice.id] = new_value #prevent the residual amount on the invoice to be less than 0 - result[invoice.id] = max(result[invoice.id], 0.0) + result[invoice.id] = round(max(result[invoice.id], 0.0),4) return result # Give Journal Items related to the payment reconciled to this invoice @@ -178,8 +201,6 @@ def _compute_lines(self, cr, uid, ids, name, args, context=None): lines = [] if invoice.move_id: for m in invoice.move_id.line_id: - if m.account_id != invoice.account_id: - continue temp_lines = [] if m.reconcile_id: temp_lines = map(lambda x: x.id, m.reconcile_id.line_id) @@ -232,7 +253,8 @@ def _get_invoice_from_reconcile(self, cr, uid, ids, context=None): }, } _columns = { - 'name': fields.char('Description', size=64, select=True, readonly=True, states={'draft':[('readonly',False)]}), + 'co_phi_baove':fields.boolean( 'Phí bảo vệ môi trường',readonly=True, states={'draft':[('readonly',False)]}), + 'name': fields.char('Số hóa đơn', size=64, select=True, readonly=True, states={'draft':[('readonly',False)]}), 'origin': fields.char('Source Document', size=64, help="Reference of the document that produced this invoice.", readonly=True, states={'draft':[('readonly',False)]}), 'supplier_invoice_number': fields.char('Supplier Invoice Number', size=64, help="The reference of this invoice as provided by the supplier.", readonly=True, states={'draft':[('readonly',False)]}), 'type': fields.selection([ @@ -274,35 +296,56 @@ def _get_invoice_from_reconcile(self, cr, uid, ids, context=None): "The payment term may compute several due dates, for example 50% now, 50% in one month."), 'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(invoice) date.", readonly=True, states={'draft':[('readonly',False)]}), - 'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."), + 'account_id': fields.many2one('account.account', 'Account', readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."), 'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}), 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}), + 'phibaove_line': fields.one2many('account.invoice.phibaove', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}), 'move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, select=1, ondelete='restrict', help="Link to the automatically generated Journal Items."), 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Subtotal', track_visibility='always', store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), + 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','co_phi_baove'], 20), 'account.invoice.tax': (_get_invoice_tax, None, 20), 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), }, multi='all'), - 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Tax', + 'amount_tax': fields.function(_amount_all, digits=(12,0), string='Tax', store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), + 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','co_phi_baove'], 20), 'account.invoice.tax': (_get_invoice_tax, None, 20), 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), }, multi='all'), 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total', store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), + 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','co_phi_baove'], 20), + 'account.invoice.tax': (_get_invoice_tax, None, 20), + 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), + }, + multi='all'), + 'phi_baove_moitruong': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Phí bảo vệ môi trường', + store={ + 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','co_phi_baove'], 20), + 'account.invoice.tax': (_get_invoice_tax, None, 20), + 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), + }, + multi='all'), + 'tien_hang': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Tiền hàng', + store={ + 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','co_phi_baove'], 20), + 'account.invoice.tax': (_get_invoice_tax, None, 20), + 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), + }, + multi='all'), + 'chiet_khau': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Chiết khấu', + store={ + 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','co_phi_baove'], 20), 'account.invoice.tax': (_get_invoice_tax, None, 20), 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), }, multi='all'), 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}, track_visibility='always'), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}, - domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]"), + 'journal_id': fields.many2one('account.journal', 'Journal', readonly=True, states={'draft':[('readonly',False)]}), 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}), 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}), 'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean', @@ -323,10 +366,16 @@ def _get_invoice_from_reconcile(self, cr, uid, ids, context=None): 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50), }, help="Remaining amount due."), - 'payment_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments', groups='base.group_user'), + 'payment_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments'), 'move_name': fields.char('Journal Entry', size=64, readonly=True, states={'draft':[('readonly',False)]}), 'user_id': fields.many2one('res.users', 'Salesperson', readonly=True, track_visibility='onchange', states={'draft':[('readonly',False)]}), - 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}) + 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}), + 'so_hoa_don': fields.char('Số hóa đơn', size=500, select=True, readonly=True, states={'draft':[('readonly',False)]}), + 'loai_hoadon': fields.selection([ + ('thongthuong', 'Thông thường'), + ('noibo', 'Nội bộ'), + ], 'Loại hóa đơn',readonly=True), + 'check':fields.boolean('Đã cập nhật phí BVMT',readonly=True, states={'draft':[('readonly',False)]}), } _defaults = { 'type': _get_type, @@ -355,22 +404,13 @@ def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, if context.get('active_model', '') in ['res.partner'] and context.get('active_ids', False) and context['active_ids']: partner = self.pool.get(context['active_model']).read(cr, uid, context['active_ids'], ['supplier','customer'])[0] if not view_type: - try: - view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_tree')[1] - except ValueError: - view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'account.invoice.tree')], limit=1) + view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'account.invoice.tree')]) view_type = 'tree' if view_type == 'form': if partner['supplier'] and not partner['customer']: - try: - view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_supplier_form')[1] - except ValueError: - view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.supplier.form')], limit=1) + view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.supplier.form')]) elif partner['customer'] and not partner['supplier']: - try: - view_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'invoice_form')[1] - except ValueError: - view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.form')], limit=1) + view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.form')]) if view_id and isinstance(view_id, (list, tuple)): view_id = view_id[0] res = super(account_invoice,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) @@ -576,7 +616,7 @@ def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date if isinstance(ids, (int, long)): ids = [ids] if not date_invoice: - date_invoice = fields.date.context_today(self, cr, uid) + date_invoice = time.strftime('%Y-%m-%d') if not payment_term_id: inv = self.browse(cr, uid, ids[0]) #To make sure the invoice due date should contain due date which is entered by user when there is no payment term defined @@ -587,7 +627,7 @@ def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date pterm_list.sort() res = {'value':{'date_due': pterm_list[-1]}} else: - raise osv.except_osv(_('Insufficient Data!'), _('The payment term of supplier does not have a payment term line.')) + raise osv.except_osv(_('Insufficient Data!'), _('The payment term of supplier does not have a payment term line.')) return res def onchange_invoice_line(self, cr, uid, ids, lines): @@ -746,17 +786,30 @@ def test_paid(self, cr, uid, ids, *args): def button_reset_taxes(self, cr, uid, ids, context=None): if context is None: context = {} + check=False ctx = context.copy() ait_obj = self.pool.get('account.invoice.tax') + phibaove_obj = self.pool.get('account.invoice.phibaove') for id in ids: + cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s AND manual is False", (id,)) partner = self.browse(cr, uid, id, context=ctx).partner_id + if partner.lang: ctx.update({'lang': partner.lang}) for taxe in ait_obj.compute(cr, uid, id, context=ctx).values(): ait_obj.create(cr, uid, taxe) + co_phi_baove=self.browse(cr, uid, id, context=ctx).co_phi_baove + if co_phi_baove: + cr.execute("DELETE FROM account_invoice_phibaove WHERE invoice_id=%s AND manual is False", (id,)) + partner = self.browse(cr, uid, id, context=ctx).partner_id + if partner.lang: + ctx.update({'lang': partner.lang}) + for phibv in phibaove_obj.compute(cr, uid, id, context=ctx).values(): + phibaove_obj.create(cr, uid, phibv) + check=True # Update the stored value (fields.function), so we write to trigger recompute - self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=ctx) + self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[],'check':check}, context=ctx) return True def button_compute(self, cr, uid, ids, context=None, set_total=False): @@ -806,6 +859,11 @@ def _get_analytic_lines(self, cr, uid, id, context=None): def action_date_assign(self, cr, uid, ids, *args): for inv in self.browse(cr, uid, ids): + co_phi_baove=inv.co_phi_baove + if co_phi_baove: + check= inv.check + if check==False: + raise osv.except_osv(_('Lỗi!'), _('Phí bảo vệ môi trường chưa được cập nhật vào tài khoản. Hãy nhấn vào link cập nhật trước khi xác nhận hóa đơn này.')) res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice) if res and res['value']: self.write(cr, uid, [inv.id], res['value']) @@ -831,13 +889,12 @@ def check_tax_lines(self, cr, uid, inv, compute_taxes, ait_obj): for tax in inv.tax_line: if tax.manual: continue - key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id) + key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id, tax.account_analytic_id.id) tax_key.append(key) if not key in compute_taxes: raise osv.except_osv(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !')) base = compute_taxes[key]['base'] - precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account') - if float_compare(abs(base - tax.base), company_currency.rounding, precision_digits=precision) == 1: + if abs(base - tax.base) > company_currency.rounding: raise osv.except_osv(_('Warning!'), _('Tax base different!\nClick on compute to update the tax base.')) for key in compute_taxes: if not key in tax_key: @@ -851,7 +908,7 @@ def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_mo cur_obj = self.pool.get('res.currency') for i in invoice_move_lines: if inv.currency_id.id != company_currency: - context.update({'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}) + context.update({'date': inv.date_invoice or time.strftime('%Y-%m-%d')}) i['currency_id'] = inv.currency_id.id i['amount_currency'] = i['price'] i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, @@ -905,6 +962,7 @@ def group_lines(self, cr, uid, iml, line, inv): def action_move_create(self, cr, uid, ids, context=None): """Creates invoice related analytics and financial move lines""" ait_obj = self.pool.get('account.invoice.tax') + phibaove_obj = self.pool.get('account.invoice.phibaove') cur_obj = self.pool.get('res.currency') period_obj = self.pool.get('account.period') payment_term_obj = self.pool.get('account.payment.term') @@ -923,8 +981,7 @@ def action_move_create(self, cr, uid, ids, context=None): ctx = context.copy() ctx.update({'lang': inv.partner_id.lang}) if not inv.date_invoice: - self.write(cr, uid, [inv.id], {'date_invoice': fields.date.context_today(self, cr, uid, context=context)}, context=ctx) - inv.refresh() + self.write(cr, uid, [inv.id], {'date_invoice': fields.date.context_today(self,cr,uid,context=context)}, context=ctx) company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id # create the analytical lines # one move line per invoice line @@ -934,7 +991,9 @@ def action_move_create(self, cr, uid, ids, context=None): self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj) # I disabled the check_total feature - if self.pool['res.users'].has_group(cr, uid, 'account.group_supplier_inv_check_total'): + group_check_total_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'group_supplier_inv_check_total')[1] + group_check_total = self.pool.get('res.groups').browse(cr, uid, group_check_total_id, context=context) + if group_check_total and uid in [x.id for x in group_check_total.users]: if (inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0)): raise osv.except_osv(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.')) @@ -952,6 +1011,23 @@ def action_move_create(self, cr, uid, ids, context=None): # one move line per tax line iml += ait_obj.move_line_get(cr, uid, inv.id) + entry_type = '' + if inv.type in ('in_invoice', 'in_refund'): + ref = inv.reference + entry_type = 'journal_pur_voucher' + if inv.type == 'in_refund': + entry_type = 'cont_voucher' + else: + ref = self._convert_ref(cr, uid, inv.number) + entry_type = 'journal_sale_vou' + if inv.type == 'out_refund': + entry_type = 'cont_voucher' + + diff_currency_p = inv.currency_id.id <> company_currency + # mot dong cho phi bao ve moi truong + + iml += phibaove_obj.move_line_get(cr, uid, inv.id) + entry_type = '' if inv.type in ('in_invoice', 'in_refund'): ref = inv.reference @@ -1018,9 +1094,11 @@ def action_move_create(self, cr, uid, ids, context=None): 'ref': ref }) + date = inv.date_invoice or time.strftime('%Y-%m-%d') + part = self.pool.get("res.partner")._find_accounting_partner(inv.partner_id) - line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part.id, inv.date_invoice, context=ctx)),iml) + line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part.id, date, context=ctx)),iml) line = self.group_lines(cr, uid, iml, line, inv) @@ -1036,7 +1114,7 @@ def action_move_create(self, cr, uid, ids, context=None): 'ref': inv.reference and inv.reference or inv.name, 'line_id': line, 'journal_id': journal_id, - 'date': inv.date_invoice, + 'date': date, 'narration': inv.comment, 'company_id': inv.company_id.id, } @@ -1052,9 +1130,7 @@ def action_move_create(self, cr, uid, ids, context=None): i[2]['period_id'] = period_id ctx.update(invoice=inv) - ctx_copy = ctx.copy() - ctx_copy.pop('lang', None) - move_id = move_obj.create(cr, uid, move, context=ctx_copy) + move_id = move_obj.create(cr, uid, move, context=ctx) new_move_name = move_obj.browse(cr, uid, move_id, context=ctx).name # make the invoice point to that move self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name}, context=ctx) @@ -1066,6 +1142,40 @@ def action_move_create(self, cr, uid, ids, context=None): def invoice_validate(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state':'open'}, context=context) + + for data in self.browse(cr, uid, ids, context): + if data.lenh_xuat_hang_id: + tien=0 + ck=0 + account_name=data.so_hoa_don + if account_name==None or account_name==False or account_name=='': + raise osv.except_osv(_("Thông báo!"), _("Vui lòng nhập số hóa đơn trước khi xác nhận .")) + if account_name==False: + account_name='Hóa đơn' + tien_hang=data.tien_hang + chiet_khau=data.chiet_khau + if tien_hang!=0: + tien=- tien_hang + if chiet_khau!=0: + ck=- chiet_khau + hop_dong_id=data.lenh_xuat_hang_id.hopdong_id.id + ngay_batdau=data.date_invoice + # ngay_ketthuc=data.lenh_xuat_hang_id.ngay_kethuc + if hop_dong_id: + dambao=False + from datetime import datetime + tn=datetime.strptime(ngay_batdau,'%Y-%m-%d') + today =time.strftime('%Y-%m-%d') + td=datetime.strptime(today,'%Y-%m-%d') + if tn>= td: + dambao=True + if tien!=0: + hinhthuc_id1=self.pool.get('icsc.hinhthuc').create(cr, uid, {'name':account_name,'amount':tien,'sale':data.lenh_xuat_hang_id.id}, context) + self.pool.get('icsc.hopdong.banhang.hinhthuc').create(cr, uid, {'name':hinhthuc_id1,'amount':tien,'hop_dong_id':hop_dong_id,'ngay_batdau':ngay_batdau,'dam_bao':dambao}, context) + if ck!=0: + hinhthuc_id2=self.pool.get('icsc.hinhthuc').create(cr, uid, {'name':account_name,'amount':ck,'sale':data.lenh_xuat_hang_id.id}, context) + + self.pool.get('icsc.hopdong.banhang.hinhthuc').create(cr, uid, {'name':hinhthuc_id2,'amount':ck,'hop_dong_id':hop_dong_id,'ngay_batdau':ngay_batdau,'dam_bao':dambao}, context) return True def line_get_convert(self, cr, uid, x, part, date, context=None): @@ -1127,6 +1237,24 @@ def action_number(self, cr, uid, ids, context=None): def action_cancel(self, cr, uid, ids, context=None): if context is None: context = {} + for hd in self.browse(cr, uid, ids, context): + so_hoa_don=hd.so_hoa_don + if hd.so_hoa_don is False: + so_hoa_don=hd.so_hoa_don + else: + so_hoa_don=str(hd.so_hoa_don.encode("utf-8")) + _logger.info('HHHHHHHHHHHHHHHHHHHHHHHHHH %s', so_hoa_don) + from datetime import datetime + if so_hoa_don: + query="""select bhht.id from icsc_hopdong_banhang_hinhthuc bhht + left join icsc_hinhthuc ht on ht.id=bhht.name + where ht.name= '"""+so_hoa_don+"""'""" + cr.execute(query) + for item in cr.dictfetchall(): + bh_ht_id=item['id'] + today =time.strftime('%Y-%m-%d') + ngay_kethuc=datetime.strptime(today,'%Y-%m-%d') + self.pool.get('icsc.hopdong.banhang.hinhthuc').write(cr, uid, [bh_ht_id],{'ngay_kethuc':ngay_kethuc}, context) account_move_obj = self.pool.get('account.move') invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids']) move_ids = [] # ones that we will need to remove @@ -1176,7 +1304,7 @@ def name_get(self, cr, uid, ids, context=None): 'out_refund': _('Refund'), 'in_refund': _('Supplier Refund'), } - return [(r['id'], '%s %s' % (r['number'] or types[r['type']], r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')] + return [(r['id'], '[%s] %s' % (r['so_hoa_don'] or r['number'] or types[r['type']], r['name'] or '')) for r in self.read(cr, uid, ids, [ 'so_hoa_don','type','number', 'name'], context, load='_classic_write')] def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100): if not args: @@ -1185,9 +1313,9 @@ def name_search(self, cr, user, name, args=None, operator='ilike', context=None, context = {} ids = [] if name: - ids = self.search(cr, user, [('number','=',name)] + args, limit=limit, context=context) + ids = self.search(cr, user, [('number',operator,name)] + args, limit=limit, context=context) if not ids: - ids = self.search(cr, user, [('name',operator,name)] + args, limit=limit, context=context) + ids = self.search(cr, user, [('so_hoa_don',operator,name)] + args, limit=limit, context=context) return self.name_get(cr, user, ids, context) def _refund_cleanup_lines(self, cr, uid, lines, context=None): @@ -1254,7 +1382,7 @@ def _prepare_refund(self, cr, uid, invoice, date=None, period_id=None, descripti refund_journal_ids = obj_journal.search(cr, uid, [('type','=','sale_refund')], context=context) if not date: - date = fields.date.context_today(self, cr, uid, context=context) + date = time.strftime('%Y-%m-%d') invoice_data.update({ 'type': type_dict[invoice['type']], 'date_invoice': date, @@ -1422,18 +1550,18 @@ def _price_unit_default(self, cr, uid, context=None): _description = "Invoice Line" _order = "invoice_id,sequence,id" _columns = { - 'name': fields.text('Description', required=True), + 'name': fields.text('Description'), 'origin': fields.char('Source Document', size=256, help="Reference of the document that produced this invoice."), 'sequence': fields.integer('Sequence', help="Gives the sequence of this line when displaying the invoice."), 'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True), 'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null', select=True), 'product_id': fields.many2one('product.product', 'Product', ondelete='set null', select=True), - 'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."), + 'account_id': fields.many2one('account.account', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."), 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')), 'price_subtotal': fields.function(_amount_line, string='Amount', type="float", - digits_compute= dp.get_precision('Account'), store=True), + digits=(12,0), store=True), 'quantity': fields.float('Quantity', digits_compute= dp.get_precision('Product Unit of Measure'), required=True), - 'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')), + 'discount': fields.float('Discount (%)', digits=(12,0)), 'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]), 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'), 'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True), @@ -1484,11 +1612,10 @@ def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type= raise osv.except_osv(_('No Partner Defined!'),_("You must first select a partner!") ) if not product: if type in ('in_invoice', 'in_refund'): - return {'value': {}, 'domain':{'uos_id':[]}} + return {'value': {}, 'domain':{'product_uom':[]}} else: - return {'value': {'price_unit': 0.0}, 'domain':{'uos_id':[]}} + return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) - product_uom_obj = self.pool.get('product.uom') fpos_obj = self.pool.get('account.fiscal.position') fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False @@ -1521,12 +1648,7 @@ def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type= result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id}) result['name'] = res.partner_ref - result['uos_id'] = res.uom_id.id - if uom_id: - uom = product_uom_obj.browse(cr, uid, uom_id) - if res.uom_id.category_id.id == uom.category_id.id: - result['uos_id'] = uom_id - + result['uos_id'] = uom_id or res.uom_id.id if res.description: result['name'] += '\n'+res.description @@ -1584,7 +1706,8 @@ def move_line_get(self, cr, uid, invoice_id, context=None): company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id for line in inv.invoice_line: mres = self.move_line_get_item(cr, uid, line, context) - mres['invl_id'] = line.id + if not mres: + continue res.append(mres) tax_code_found= False for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, @@ -1616,7 +1739,7 @@ def move_line_get(self, cr, uid, invoice_id, context=None): def move_line_get_item(self, cr, uid, line, context=None): return { 'type':'src', - 'name': line.name.split('\n')[0][:64], + 'name':line.name or line.product_id.name, # line.name.split('\n')[0][:64], 'price_unit':line.price_unit, 'quantity':line.quantity, 'price':line.price_subtotal, @@ -1697,20 +1820,21 @@ def base_change(self, cr, uid, ids, base, currency_id=False, company_id=False, d if company_id: company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0] if currency_id and company_currency: - base = cur_obj.compute(cr, uid, currency_id, company_currency, base*factor, context={'date': date_invoice or fields.date.context_today(self, cr, uid)}, round=False) + base = cur_obj.compute(cr, uid, currency_id, company_currency, base*factor, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False) return {'value': {'base_amount':base}} def amount_change(self, cr, uid, ids, amount, currency_id=False, company_id=False, date_invoice=False): cur_obj = self.pool.get('res.currency') company_obj = self.pool.get('res.company') company_currency = False + factor = 1 + if ids: + factor = self.read(cr, uid, ids[0], ['factor_tax'])['factor_tax'] if company_id: company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0] if currency_id and company_currency: - amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount, context={'date': date_invoice or fields.date.context_today(self, cr, uid)}, round=False) - tax_rec = self.browse(cr, uid, ids) - tax_sign = (tax_rec[0].tax_amount / tax_rec[0].amount) if tax_rec and tax_rec[0].amount else 1 - return {'value': {'tax_amount': amount * tax_sign}} + amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount*factor, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False) + return {'value': {'tax_amount': amount}} _order = 'sequence' _defaults = { @@ -1738,27 +1862,19 @@ def compute(self, cr, uid, invoice_id, context=None): if inv.type in ('out_invoice','in_invoice'): val['base_code_id'] = tax['base_code_id'] val['tax_code_id'] = tax['tax_code_id'] - val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False) - val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False) + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) val['account_id'] = tax['account_collected_id'] or line.account_id.id val['account_analytic_id'] = tax['account_analytic_collected_id'] else: val['base_code_id'] = tax['ref_base_code_id'] val['tax_code_id'] = tax['ref_tax_code_id'] - val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False) - val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)}, round=False) + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) val['account_id'] = tax['account_paid_id'] or line.account_id.id val['account_analytic_id'] = tax['account_analytic_paid_id'] - # If the taxes generate moves on the same financial account as the invoice line - # and no default analytic account is defined at the tax level, propagate the - # analytic account from the invoice line to the tax line. This is necessary - # in situations were (part of) the taxes cannot be reclaimed, - # to ensure the tax move is allocated to the proper analytic account. - if not val.get('account_analytic_id') and line.account_analytic_id and val['account_id'] == line.account_id.id: - val['account_analytic_id'] = line.account_analytic_id.id - - key = (val['tax_code_id'], val['base_code_id'], val['account_id']) + key = (val['tax_code_id'], val['base_code_id'], val['account_id'], val['account_analytic_id']) if not key in tax_grouped: tax_grouped[key] = val else: @@ -1795,7 +1911,145 @@ def move_line_get(self, cr, uid, invoice_id): }) return res +class account_invoice_phibaove(osv.osv): + _name = "account.invoice.phibaove" + _description = "Tài khoản phí bảo vệ" + + def _count_factor(self, cr, uid, ids, name, args, context=None): + res = {} + for invoice_tax in self.browse(cr, uid, ids, context=context): + res[invoice_tax.id] = { + 'factor_base': 1.0, + 'factor_tax': 1.0, + } + if invoice_tax.amount <> 0.0: + factor_tax = invoice_tax.tax_amount / invoice_tax.amount + res[invoice_tax.id]['factor_tax'] = factor_tax + + if invoice_tax.base <> 0.0: + factor_base = invoice_tax.base_amount / invoice_tax.base + res[invoice_tax.id]['factor_base'] = factor_base + + return res + + _columns = { + 'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True), + 'name': fields.char('Mô tả', size=64, required=True), + 'account_id': fields.many2one('account.account', 'Tài khoản', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]), + 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'), + 'base': fields.float('Gốc', digits_compute=dp.get_precision('Account')), + 'amount': fields.float('Phí bảo vệ', digits_compute=dp.get_precision('Account')), + 'manual': fields.boolean('Manual'), + 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of invoice tax."), + 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."), + 'base_amount': fields.float('Base Code Amount', digits_compute=dp.get_precision('Account')), + 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."), + 'tax_amount': fields.float('Tax Code Amount', digits_compute=dp.get_precision('Account')), + 'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), + 'factor_base': fields.function(_count_factor, string='Multipication factor for Base code', type='float', multi="all"), + 'factor_tax': fields.function(_count_factor, string='Multipication factor Tax code', type='float', multi="all") + } + + def base_change(self, cr, uid, ids, base, currency_id=False, company_id=False, date_invoice=False): + cur_obj = self.pool.get('res.currency') + company_obj = self.pool.get('res.company') + company_currency = False + factor = 1 + if ids: + factor = self.read(cr, uid, ids[0], ['factor_base'])['factor_base'] + if company_id: + company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0] + if currency_id and company_currency: + base = cur_obj.compute(cr, uid, currency_id, company_currency, base*factor, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False) + return {'value': {'base_amount':base}} + + def amount_change(self, cr, uid, ids, amount, currency_id=False, company_id=False, date_invoice=False): + cur_obj = self.pool.get('res.currency') + company_obj = self.pool.get('res.company') + company_currency = False + factor = 1 + if ids: + factor = self.read(cr, uid, ids[0], ['factor_tax'])['factor_tax'] + if company_id: + company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0] + if currency_id and company_currency: + amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount*factor, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False) + return {'value': {'tax_amount': amount}} + + _order = 'sequence' + _defaults = { + 'manual': 1, + 'base_amount': 0.0, + 'tax_amount': 0.0, + } + def compute(self, cr, uid, invoice_id, context=None): + tax_grouped = {} + tax_obj = self.pool.get('account.tax') + cur_obj = self.pool.get('res.currency') + inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context) + cur = inv.currency_id + company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id + for line in inv.invoice_line: + for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, line.product_id, inv.partner_id)['taxes']: + val={} + val['invoice_id'] = inv.id + val['name'] = 'Thuế bảo vệ môi trường 5%' + val['manual'] = False + val['sequence'] = tax['sequence'] + val['base'] = cur_obj.round(cr, uid, cur, tax['price_unit'] * line['quantity']) + val['amount'] =0.05 * val['base'] + if inv.type in ('out_invoice','in_invoice'): + val['base_code_id'] = tax['base_code_id'] + val['tax_code_id'] = tax['tax_code_id'] + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['account_id'] = 231 + val['account_analytic_id'] = tax['account_analytic_collected_id'] + else: + val['base_code_id'] = tax['ref_base_code_id'] + val['tax_code_id'] = tax['ref_tax_code_id'] + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['account_id'] = tax['account_paid_id'] or line.account_id.id + val['account_analytic_id'] = tax['account_analytic_paid_id'] + + key = (val['tax_code_id'], val['base_code_id'], val['account_id'], val['account_analytic_id']) + if not key in tax_grouped: + tax_grouped[key] = val + else: + tax_grouped[key]['amount'] += val['amount'] + tax_grouped[key]['base'] += val['base'] + tax_grouped[key]['base_amount'] += val['base_amount'] + tax_grouped[key]['tax_amount'] += val['tax_amount'] + + for t in tax_grouped.values(): + t['base'] = cur_obj.round(cr, uid, cur, t['base']) + t['amount'] = cur_obj.round(cr, uid, cur, t['amount']) + t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount']) + t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount']) + return tax_grouped + def move_line_get(self, cr, uid, invoice_id): + res = [] + cr.execute('SELECT * FROM account_invoice_phibaove WHERE invoice_id=%s', (invoice_id,)) + for t in cr.dictfetchall(): + if not t['amount'] \ + and not t['tax_code_id'] \ + and not t['tax_amount']: + continue + res.append({ + 'type':'tax', + 'name':t['name'], + 'price_unit': t['amount'], + 'quantity': 1, + 'price': t['amount'] or 0.0, + 'account_id': t['account_id'], + 'tax_code_id': t['tax_code_id'], + 'tax_amount': t['tax_amount'], + 'account_analytic_id': t['account_analytic_id'], + }) + return res +account_invoice_phibaove() class res_partner(osv.osv): """ Inherits partner and adds invoice information in the partner form """ _inherit = 'res.partner' @@ -1815,10 +2069,10 @@ def _find_accounting_partner(self, partner): partner = partner.parent_id return partner - def copy_data(self, cr, uid, id, default=None, context=None): + def copy(self, cr, uid, id, default=None, context=None): default = default or {} default.update({'invoice_ids' : []}) - return super(res_partner, self).copy_data(cr, uid, id, default=default, context=context) + return super(res_partner, self).copy(cr, uid, id, default, context) class mail_compose_message(osv.Model): diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index fb3a49dc9de21..2bfe6a4a01ce7 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -60,8 +60,8 @@ - - + + @@ -95,7 +95,7 @@ - + @@ -116,22 +116,25 @@ account.invoice + - - + + + - + - - + + - - - - + + + + + @@ -163,6 +166,7 @@ + - + on_change="onchange_journal_id(journal_id, context)" widget="selection"/> + @@ -202,7 +205,7 @@ domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '=', 'other')]" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/> + domain="[('type','!=','view'), ('company_id', '=', parent.company_id)]"/> @@ -253,8 +256,8 @@ - - + + @@ -294,13 +297,13 @@
- diff --git a/addons/account_data.xml b/addons/account_data.xml new file mode 100644 index 0000000000000..8119e9f1002e6 --- /dev/null +++ b/addons/account_data.xml @@ -0,0 +1,165 @@ + + + + + + Sales + sale + + + + + Immediate Payment + Immediate Payment + + + + balance + + + + + + + + 15 Days + 15 Days + + + + balance + + + + + + + Payment Term + 6 + + + + 30 Net Days + 30 Net Days + + + + balance + + + + + + + + + Account Default Sales Journal + + SAJ/%(year)s/ + + + Account Default Sales Credit Note Journal + + SCNJ/%(year)s/ + + + Account Default Expenses Journal + + EXJ/%(year)s/ + + + Account Default Expenses Credit Notes Journal + + ECNJ/%(year)s/ + + + Account Default Bank Journal + + BNK/%(year)s/ + + + Account Default Checks Journal + + CHK/%(year)s/ + + + Account Default Cash Journal + + CSH/%(year)s/ + + + Account Default Opening Entries Journal + + OPEJ/%(year)s/ + + + Account Default Miscellaneous Journal + + MISJ/%(year)s/ + + + + + Account Reconcile + account.reconcile + + + Account reconcile sequence + account.reconcile + A + + + + + + Account Bank Statement + account.bank.statement + + + Account Bank Statement + account.bank.statement + St. %(month)s/%(day)s/ + + + + + + Account Cash Statement + account.cash.statement + + + Account Cash Statement + account.cash.statement + Cr. %(month)s/%(day)s/ + + + + + + Invoice + account.invoice + + + + + Validated + account.invoice + + Invoice validated + + + Paid + account.invoice + + Invoice paid + + + diff --git a/addons/account_followup/account_followup.py b/addons/account_followup/account_followup.py index c48bab0f25632..1ab8bdadb23fd 100644 --- a/addons/account_followup/account_followup.py +++ b/addons/account_followup/account_followup.py @@ -1,478 +1,365 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -from lxml import etree - -from openerp.tools.translate import _ - -class followup(osv.osv): - _name = 'account_followup.followup' - _description = 'Account Follow-up' - _rec_name = 'name' - _columns = { - 'followup_line': fields.one2many('account_followup.followup.line', 'followup_id', 'Follow-up'), - 'company_id': fields.many2one('res.company', 'Company', required=True), - 'name': fields.related('company_id', 'name', string = "Name"), - } - _defaults = { - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'account_followup.followup', context=c), - } - _sql_constraints = [('company_uniq', 'unique(company_id)', 'Only one follow-up per company is allowed')] - - -class followup_line(osv.osv): - - def _get_default_template(self, cr, uid, ids, context=None): - try: - return self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_followup', 'email_template_account_followup_default')[1] - except ValueError: - return False - - _name = 'account_followup.followup.line' - _description = 'Follow-up Criteria' - _columns = { - 'name': fields.char('Follow-Up Action', size=64, required=True), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of follow-up lines."), - 'delay': fields.integer('Due Days', help="The number of days after the due date of the invoice to wait before sending the reminder. Could be negative if you want to send a polite alert beforehand.", required=True), - 'followup_id': fields.many2one('account_followup.followup', 'Follow Ups', required=True, ondelete="cascade"), - 'description': fields.text('Printed Message', translate=True), - 'send_email':fields.boolean('Send an Email', help="When processing, it will send an email"), - 'send_letter':fields.boolean('Send a Letter', help="When processing, it will print a letter"), - 'manual_action':fields.boolean('Manual Action', help="When processing, it will set the manual action to be taken for that customer. "), - 'manual_action_note':fields.text('Action To Do', placeholder="e.g. Give a phone call, check with others , ..."), - 'manual_action_responsible_id':fields.many2one('res.users', 'Assign a Responsible', ondelete='set null'), - 'email_template_id':fields.many2one('email.template', 'Email Template', ondelete='set null'), - } - _order = 'delay' - _sql_constraints = [('days_uniq', 'unique(followup_id, delay)', 'Days of the follow-up levels must be different')] - _defaults = { - 'send_email': True, - 'send_letter': True, - 'manual_action':False, - 'description': """ - Dear %(partner_name)s, - -Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take appropriate measures in order to carry out this payment in the next 8 days. - -Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department. - -Best Regards, -""", - 'email_template_id': _get_default_template, - } - - - def _check_description(self, cr, uid, ids, context=None): - for line in self.browse(cr, uid, ids, context=context): - if line.description: - try: - line.description % {'partner_name': '', 'date':'', 'user_signature': '', 'company_name': ''} - except: - return False - return True - - _constraints = [ - (_check_description, 'Your description is invalid, use the right legend or %% if you want to use the percent character.', ['description']), - ] - - -class account_move_line(osv.osv): - - def _get_result(self, cr, uid, ids, name, arg, context=None): - res = {} - for aml in self.browse(cr, uid, ids, context=context): - res[aml.id] = aml.debit - aml.credit - return res - - _inherit = 'account.move.line' - _columns = { - 'followup_line_id': fields.many2one('account_followup.followup.line', 'Follow-up Level', - ondelete='restrict'), #restrict deletion of the followup line - 'followup_date': fields.date('Latest Follow-up', select=True), - 'result':fields.function(_get_result, type='float', method=True, - string="Balance") #'balance' field is not the same - } - - -class res_partner(osv.osv): - - def fields_view_get(self, cr, uid, view_id=None, view_type=None, context=None, toolbar=False, submenu=False): - res = super(res_partner, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, - toolbar=toolbar, submenu=submenu) - context = context or {} - if view_type == 'form' and context.get('Followupfirst'): - doc = etree.XML(res['arch'], parser=None, base_url=None) - first_node = doc.xpath("//page[@name='followup_tab']") - root = first_node[0].getparent() - root.insert(0, first_node[0]) - res['arch'] = etree.tostring(doc, encoding="utf-8") - return res - - def _get_latest(self, cr, uid, ids, names, arg, context=None, company_id=None): - res={} - if company_id == None: - company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id - else: - company = self.pool.get('res.company').browse(cr, uid, company_id, context=context) - for partner in self.browse(cr, uid, ids, context=context): - amls = partner.unreconciled_aml_ids - latest_date = False - latest_level = False - latest_days = False - latest_level_without_lit = False - latest_days_without_lit = False - for aml in amls: - if (aml.company_id == company) and (aml.followup_line_id != False) and (not latest_days or latest_days < aml.followup_line_id.delay): - latest_days = aml.followup_line_id.delay - latest_level = aml.followup_line_id.id - if (aml.company_id == company) and (not latest_date or latest_date < aml.followup_date): - latest_date = aml.followup_date - if (aml.company_id == company) and (aml.blocked == False) and (aml.followup_line_id != False and - (not latest_days_without_lit or latest_days_without_lit < aml.followup_line_id.delay)): - latest_days_without_lit = aml.followup_line_id.delay - latest_level_without_lit = aml.followup_line_id.id - res[partner.id] = {'latest_followup_date': latest_date, - 'latest_followup_level_id': latest_level, - 'latest_followup_level_id_without_lit': latest_level_without_lit} - return res - - def do_partner_manual_action(self, cr, uid, partner_ids, context=None): - #partner_ids -> res.partner - for partner in self.browse(cr, uid, partner_ids, context=context): - #Check action: check if the action was not empty, if not add - action_text= "" - if partner.payment_next_action: - action_text = (partner.payment_next_action or '') + "\n" + (partner.latest_followup_level_id_without_lit.manual_action_note or '') - else: - action_text = partner.latest_followup_level_id_without_lit.manual_action_note or '' - - #Check date: only change when it did not exist already - action_date = partner.payment_next_action_date or fields.date.context_today(self, cr, uid, context=context) - - # Check responsible: if partner has not got a responsible already, take from follow-up - responsible_id = False - if partner.payment_responsible_id: - responsible_id = partner.payment_responsible_id.id - else: - p = partner.latest_followup_level_id_without_lit.manual_action_responsible_id - responsible_id = p and p.id or False - self.write(cr, uid, [partner.id], {'payment_next_action_date': action_date, - 'payment_next_action': action_text, - 'payment_responsible_id': responsible_id}) - - def do_partner_print(self, cr, uid, wizard_partner_ids, data, context=None): - #wizard_partner_ids are ids from special view, not from res.partner - if not wizard_partner_ids: - return {} - data['partner_ids'] = wizard_partner_ids - datas = { - 'ids': [], - 'model': 'account_followup.followup', - 'form': data - } - return { - 'type': 'ir.actions.report.xml', - 'report_name': 'account_followup.followup.print', - 'datas': datas, - } - - def do_partner_mail(self, cr, uid, partner_ids, context=None): - if context is None: - context = {} - ctx = context.copy() - ctx['followup'] = True - #partner_ids are res.partner ids - # If not defined by latest follow-up level, it will be the default template if it can find it - mtp = self.pool.get('email.template') - unknown_mails = 0 - for partner in self.browse(cr, uid, partner_ids, context=ctx): - if partner.email and partner.email.strip(): - level = partner.latest_followup_level_id_without_lit - if level and level.send_email and level.email_template_id and level.email_template_id.id: - mtp.send_mail(cr, uid, level.email_template_id.id, partner.id, context=ctx) - else: - mail_template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, - 'account_followup', 'email_template_account_followup_default') - mtp.send_mail(cr, uid, mail_template_id[1], partner.id, context=ctx) - else: - unknown_mails = unknown_mails + 1 - action_text = _("Email not sent because of email address of partner not filled in") - if partner.payment_next_action_date: - payment_action_date = min(fields.date.context_today(self, cr, uid, context=ctx), partner.payment_next_action_date) - else: - payment_action_date = fields.date.context_today(self, cr, uid, context=ctx) - if partner.payment_next_action: - payment_next_action = partner.payment_next_action + " \n " + action_text - else: - payment_next_action = action_text - self.write(cr, uid, [partner.id], {'payment_next_action_date': payment_action_date, - 'payment_next_action': payment_next_action}, context=ctx) - return unknown_mails - - def get_followup_table_html(self, cr, uid, ids, context=None): - """ Build the html tables to be included in emails send to partners, - when reminding them their overdue invoices. - :param ids: [id] of the partner for whom we are building the tables - :rtype: string - """ - from report import account_followup_print - - assert len(ids) == 1 - if context is None: - context = {} - partner = self.browse(cr, uid, ids[0], context=context) - #copy the context to not change global context. Overwrite it because _() looks for the lang in local variable 'context'. - #Set the language to use = the partner language - context = dict(context, lang=partner.lang) - followup_table = '' - if partner.unreconciled_aml_ids: - company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id - current_date = fields.date.context_today(self, cr, uid, context=context) - rml_parse = account_followup_print.report_rappel(cr, uid, "followup_rml_parser") - final_res = rml_parse._lines_get_with_partner(partner, company.id) - - for currency_dict in final_res: - currency = currency_dict.get('line', [{'currency_id': company.currency_id}])[0]['currency_id'] - followup_table += ''' - - - - - - - - - - ''' - total = 0 - for aml in currency_dict['line']: - block = aml['blocked'] and 'X' or ' ' - total += aml['balance'] - strbegin = "" - date = aml['date_maturity'] or aml['date'] - if date <= current_date and aml['balance'] > 0: - strbegin = "" - followup_table +="" + strbegin + str(aml['date']) + strend + strbegin + aml['name'] + strend + strbegin + (aml['ref'] or '') + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "" - total = rml_parse.formatLang(total, dp='Account', currency_obj=currency) - followup_table += ''' -
''' + _("Invoice Date") + '''''' + _("Description") + '''''' + _("Reference") + '''''' + _("Due Date") + '''''' + _("Amount") + " (%s)" % (currency.symbol) + '''''' + _("Lit.") + '''
" - strend = "" - strend = "
-
''' + _("Amount due") + ''' : %s
''' % (total) - return followup_table - - def write(self, cr, uid, ids, vals, context=None): - if vals.get("payment_responsible_id", False): - for part in self.browse(cr, uid, ids, context=context): - if part.payment_responsible_id <> vals["payment_responsible_id"]: - #Find partner_id of user put as responsible - responsible_partner_id = self.pool.get("res.users").browse(cr, uid, vals['payment_responsible_id'], context=context).partner_id.id - self.pool.get("mail.thread").message_post(cr, uid, 0, - body = _("You became responsible to do the next action for the payment follow-up of") + " " + part.name + " ", - type = 'comment', - subtype = "mail.mt_comment", context = context, - model = 'res.partner', res_id = part.id, - partner_ids = [responsible_partner_id]) - return super(res_partner, self).write(cr, uid, ids, vals, context=context) - - def copy(self, cr, uid, record_id, default=None, context=None): - if default is None: - default = {} - - default.update({'unreconciled_aml_ids': []}) - return super(res_partner, self).copy(cr, uid, record_id, default, context) - - def action_done(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'payment_next_action_date': False, 'payment_next_action':'', 'payment_responsible_id': False}, context=context) - - def do_button_print(self, cr, uid, ids, context=None): - assert(len(ids) == 1) - company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id - #search if the partner has accounting entries to print. If not, it may not be present in the - #psql view the report is based on, so we need to stop the user here. - if not self.pool.get('account.move.line').search(cr, uid, [ - ('partner_id', '=', ids[0]), - ('account_id.type', '=', 'receivable'), - ('reconcile_id', '=', False), - ('state', '!=', 'draft'), - ('company_id', '=', company_id), - '|', ('date_maturity', '=', False), ('date_maturity', '<=', fields.date.context_today(self, cr, uid)), - ], context=context): - raise osv.except_osv(_('Error!'),_("The partner does not have any accounting entries to print in the overdue report for the current company.")) - self.message_post(cr, uid, [ids[0]], body=_('Printed overdue payments report'), context=context) - #build the id of this partner in the psql view. Could be replaced by a search with [('company_id', '=', company_id),('partner_id', '=', ids[0])] - wizard_partner_ids = [ids[0] * 10000 + company_id] - followup_ids = self.pool.get('account_followup.followup').search(cr, uid, [('company_id', '=', company_id)], context=context) - if not followup_ids: - raise osv.except_osv(_('Error!'),_("There is no followup plan defined for the current company.")) - data = { - 'date': fields.date.today(), - 'followup_id': followup_ids[0], - } - #call the print overdue report on this partner - return self.do_partner_print(cr, uid, wizard_partner_ids, data, context=context) - - def _get_amounts_and_date(self, cr, uid, ids, name, arg, context=None): - ''' - Function that computes values for the followup functional fields. Note that 'payment_amount_due' - is similar to 'credit' field on res.partner except it filters on user's company. - ''' - res = {} - company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id - current_date = fields.date.context_today(self, cr, uid, context=context) - for partner in self.browse(cr, uid, ids, context=context): - worst_due_date = False - amount_due = amount_overdue = 0.0 - for aml in partner.unreconciled_aml_ids: - if (aml.company_id == company): - date_maturity = aml.date_maturity or aml.date - if not worst_due_date or date_maturity < worst_due_date: - worst_due_date = date_maturity - amount_due += aml.result - if (date_maturity <= current_date): - amount_overdue += aml.result - res[partner.id] = {'payment_amount_due': amount_due, - 'payment_amount_overdue': amount_overdue, - 'payment_earliest_due_date': worst_due_date} - return res - - def _get_followup_overdue_query(self, cr, uid, args, overdue_only=False, context=None): - ''' - This function is used to build the query and arguments to use when making a search on functional fields - * payment_amount_due - * payment_amount_overdue - Basically, the query is exactly the same except that for overdue there is an extra clause in the WHERE. - - :param args: arguments given to the search in the usual domain notation (list of tuples) - :param overdue_only: option to add the extra argument to filter on overdue accounting entries or not - :returns: a tuple with - * the query to execute as first element - * the arguments for the execution of this query - :rtype: (string, []) - ''' - company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id - having_where_clause = ' AND '.join(map(lambda x: '(SUM(bal2) %s %%s)' % (x[1]), args)) - having_values = [x[2] for x in args] - query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) - overdue_only_str = overdue_only and 'AND date_maturity <= NOW()' or '' - return ('''SELECT pid AS partner_id, SUM(bal2) FROM - (SELECT CASE WHEN bal IS NOT NULL THEN bal - ELSE 0.0 END AS bal2, p.id as pid FROM - (SELECT (debit-credit) AS bal, partner_id - FROM account_move_line l - WHERE account_id IN - (SELECT id FROM account_account - WHERE type=\'receivable\' AND active) - ''' + overdue_only_str + ''' - AND reconcile_id IS NULL - AND company_id = %s - AND ''' + query + ''') AS l - RIGHT JOIN res_partner p - ON p.id = partner_id ) AS pl - GROUP BY pid HAVING ''' + having_where_clause, [company_id] + having_values) - - def _payment_overdue_search(self, cr, uid, obj, name, args, context=None): - if not args: - return [] - query, query_args = self._get_followup_overdue_query(cr, uid, args, overdue_only=True, context=context) - cr.execute(query, query_args) - res = cr.fetchall() - if not res: - return [('id','=','0')] - return [('id','in', [x[0] for x in res])] - - def _payment_earliest_date_search(self, cr, uid, obj, name, args, context=None): - if not args: - return [] - company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id - having_where_clause = ' AND '.join(map(lambda x: '(MIN(l.date_maturity) %s %%s)' % (x[1]), args)) - having_values = [x[2] for x in args] - query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) - cr.execute('SELECT partner_id FROM account_move_line l '\ - 'WHERE account_id IN '\ - '(SELECT id FROM account_account '\ - 'WHERE type=\'receivable\' AND active) '\ - 'AND l.company_id = %s ' - 'AND reconcile_id IS NULL '\ - 'AND '+query+' '\ - 'AND partner_id IS NOT NULL '\ - 'GROUP BY partner_id HAVING '+ having_where_clause, - [company_id] + having_values) - res = cr.fetchall() - if not res: - return [('id','=','0')] - return [('id','in', [x[0] for x in res])] - - def _payment_due_search(self, cr, uid, obj, name, args, context=None): - if not args: - return [] - query, query_args = self._get_followup_overdue_query(cr, uid, args, overdue_only=False, context=context) - cr.execute(query, query_args) - res = cr.fetchall() - if not res: - return [('id','=','0')] - return [('id','in', [x[0] for x in res])] - - _inherit = "res.partner" - _columns = { - 'payment_responsible_id':fields.many2one('res.users', ondelete='set null', string='Follow-up Responsible', - help="Optionally you can assign a user to this field, which will make him responsible for the action.", - track_visibility="onchange"), - 'payment_note':fields.text('Customer Payment Promise', help="Payment Note", track_visibility="onchange"), - 'payment_next_action':fields.text('Next Action', - help="This is the next action to be taken. It will automatically be set when the partner gets a follow-up level that requires a manual action. ", - track_visibility="onchange"), - 'payment_next_action_date':fields.date('Next Action Date', - help="This is when the manual follow-up is needed. " \ - "The date will be set to the current date when the partner gets a follow-up level that requires a manual action. "\ - "Can be practical to set manually e.g. to see if he keeps his promises."), - 'unreconciled_aml_ids':fields.one2many('account.move.line', 'partner_id', domain=['&', ('reconcile_id', '=', False), '&', - ('account_id.active','=', True), '&', ('account_id.type', '=', 'receivable'), ('state', '!=', 'draft')]), - 'latest_followup_date':fields.function(_get_latest, method=True, type='date', string="Latest Follow-up Date", - help="Latest date that the follow-up level of the partner was changed", - store=False, multi="latest"), - 'latest_followup_level_id':fields.function(_get_latest, method=True, - type='many2one', relation='account_followup.followup.line', string="Latest Follow-up Level", - help="The maximum follow-up level", - store=False, - multi="latest"), - 'latest_followup_level_id_without_lit':fields.function(_get_latest, method=True, - type='many2one', relation='account_followup.followup.line', string="Latest Follow-up Level without litigation", - help="The maximum follow-up level without taking into account the account move lines with litigation", - store=False, - multi="latest"), - 'payment_amount_due':fields.function(_get_amounts_and_date, - type='float', string="Amount Due", - store = False, multi="followup", - fnct_search=_payment_due_search), - 'payment_amount_overdue':fields.function(_get_amounts_and_date, - type='float', string="Amount Overdue", - store = False, multi="followup", - fnct_search = _payment_overdue_search), - 'payment_earliest_due_date':fields.function(_get_amounts_and_date, - type='date', - string = "Worst Due Date", - multi="followup", - fnct_search=_payment_earliest_date_search), - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +from lxml import etree +from openerp.tools.translate import _ + +class followup(osv.osv): + _name = 'account_followup.followup' + _description = 'Account Follow-up' + _rec_name = 'name' + _columns = {'followup_line': fields.one2many('account_followup.followup.line', 'followup_id', 'Follow-up'), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'name': fields.related('company_id', 'name', string='Name')} + _defaults = {'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'account_followup.followup', context=c)} + _sql_constraints = [('company_uniq', 'unique(company_id)', 'Only one follow-up per company is allowed')] + + +class followup_line(osv.osv): + + def _get_default_template(self, cr, uid, ids, context = None): + try: + return self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_followup', 'email_template_account_followup_default')[1] + except ValueError: + return False + + _name = 'account_followup.followup.line' + _description = 'Follow-up Criteria' + _columns = {'name': fields.char('Follow-Up Action', size=64, required=True), + 'sequence': fields.integer('Sequence', help='Gives the sequence order when displaying a list of follow-up lines.'), + 'delay': fields.integer('Due Days', help='The number of days after the due date of the invoice to wait before sending the reminder. Could be negative if you want to send a polite alert beforehand.', required=True), + 'followup_id': fields.many2one('account_followup.followup', 'Follow Ups', required=True, ondelete='cascade'), + 'description': fields.text('Printed Message', translate=True), + 'send_email': fields.boolean('Send an Email', help='When processing, it will send an email'), + 'send_letter': fields.boolean('Send a Letter', help='When processing, it will print a letter'), + 'manual_action': fields.boolean('Manual Action', help='When processing, it will set the manual action to be taken for that customer. '), + 'manual_action_note': fields.text('Action To Do', placeholder='e.g. Give a phone call, check with others , ...'), + 'manual_action_responsible_id': fields.many2one('res.users', 'Assign a Responsible', ondelete='set null'), + 'email_template_id': fields.many2one('email.template', 'Email Template', ondelete='set null')} + _order = 'delay' + _sql_constraints = [('days_uniq', 'unique(followup_id, delay)', 'Days of the follow-up levels must be different')] + _defaults = {'send_email': True, + 'send_letter': True, + 'manual_action': False, + 'description': '\n Dear %(partner_name)s,\n\nException made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take appropriate measures in order to carry out this payment in the next 8 days.\n\nWould your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department.\n\nBest Regards,\n', + 'email_template_id': _get_default_template} + + def _check_description(self, cr, uid, ids, context = None): + for line in self.browse(cr, uid, ids, context=context): + if line.description: + try: + line.description % {'partner_name': '', + 'date': '', + 'user_signature': '', + 'company_name': ''} + except: + return False + + return True + + _constraints = [(_check_description, 'Your description is invalid, use the right legend or %% if you want to use the percent character.', ['description'])] + + +class account_move_line(osv.osv): + + def _get_result(self, cr, uid, ids, name, arg, context = None): + res = {} + for aml in self.browse(cr, uid, ids, context=context): + res[aml.id] = aml.debit - aml.credit + + return res + + _inherit = 'account.move.line' + _columns = {'followup_line_id': fields.many2one('account_followup.followup.line', 'Follow-up Level', ondelete='restrict'), + 'followup_date': fields.date('Latest Follow-up', select=True), + 'result': fields.function(_get_result, type='float', method=True, string='Balance')} + + +class res_partner(osv.osv): + + def fields_view_get(self, cr, uid, view_id = None, view_type = None, context = None, toolbar = False, submenu = False): + res = super(res_partner, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) + context = context or {} + if view_type == 'form' and context.get('Followupfirst'): + doc = etree.XML(res['arch'], parser=None, base_url=None) + first_node = doc.xpath("//page[@name='followup_tab']") + root = first_node[0].getparent() + root.insert(0, first_node[0]) + res['arch'] = etree.tostring(doc, encoding='utf-8') + return res + + def _get_latest(self, cr, uid, ids, names, arg, context = None, company_id = None): + res = {} + if company_id == None: + company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id + else: + company = self.pool.get('res.company').browse(cr, uid, company_id, context=context) + for partner in self.browse(cr, uid, ids, context=context): + amls = partner.unreconciled_aml_ids + latest_date = False + latest_level = False + latest_days = False + latest_level_without_lit = False + latest_days_without_lit = False + for aml in amls: + if aml.company_id == company and aml.followup_line_id != False and (not latest_days or latest_days < aml.followup_line_id.delay): + latest_days = aml.followup_line_id.delay + latest_level = aml.followup_line_id.id + if aml.company_id == company and (not latest_date or latest_date < aml.followup_date): + latest_date = aml.followup_date + if aml.company_id == company and aml.blocked == False and aml.followup_line_id != False and (not latest_days_without_lit or latest_days_without_lit < aml.followup_line_id.delay): + latest_days_without_lit = aml.followup_line_id.delay + latest_level_without_lit = aml.followup_line_id.id + + res[partner.id] = {'latest_followup_date': latest_date, + 'latest_followup_level_id': latest_level, + 'latest_followup_level_id_without_lit': latest_level_without_lit} + + return res + + def do_partner_manual_action(self, cr, uid, partner_ids, context = None): + for partner in self.browse(cr, uid, partner_ids, context=context): + action_text = '' + if partner.payment_next_action: + action_text = (partner.payment_next_action or '') + '\n' + (partner.latest_followup_level_id_without_lit.manual_action_note or '') + else: + action_text = partner.latest_followup_level_id_without_lit.manual_action_note or '' + action_date = partner.payment_next_action_date or fields.date.context_today(self, cr, uid, context=context) + responsible_id = False + if partner.payment_responsible_id: + responsible_id = partner.payment_responsible_id.id + else: + p = partner.latest_followup_level_id_without_lit.manual_action_responsible_id + responsible_id = p and p.id or False + self.write(cr, uid, [partner.id], {'payment_next_action_date': action_date, + 'payment_next_action': action_text, + 'payment_responsible_id': responsible_id}) + + def do_partner_print(self, cr, uid, wizard_partner_ids, data, context = None): + if not wizard_partner_ids: + return {} + data['partner_ids'] = wizard_partner_ids + datas = {'ids': [], + 'model': 'account_followup.followup', + 'form': data} + return {'type': 'ir.actions.report.xml', + 'report_name': 'account_followup.followup.print', + 'datas': datas} + + def do_partner_mail(self, cr, uid, partner_ids, context = None): + if context is None: + context = {} + ctx = context.copy() + ctx['followup'] = True + mtp = self.pool.get('email.template') + unknown_mails = 0 + for partner in self.browse(cr, uid, partner_ids, context=ctx): + if partner.email and partner.email.strip(): + level = partner.latest_followup_level_id_without_lit + if level and level.send_email and level.email_template_id and level.email_template_id.id: + mtp.send_mail(cr, uid, level.email_template_id.id, partner.id, context=ctx) + else: + mail_template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_followup', 'email_template_account_followup_default') + mtp.send_mail(cr, uid, mail_template_id[1], partner.id, context=ctx) + else: + unknown_mails = unknown_mails + 1 + action_text = _('Email not sent because of email address of partner not filled in') + if partner.payment_next_action_date: + payment_action_date = min(fields.date.context_today(self, cr, uid, context=ctx), partner.payment_next_action_date) + else: + payment_action_date = fields.date.context_today(self, cr, uid, context=ctx) + if partner.payment_next_action: + payment_next_action = partner.payment_next_action + ' \n ' + action_text + else: + payment_next_action = action_text + self.write(cr, uid, [partner.id], {'payment_next_action_date': payment_action_date, + 'payment_next_action': payment_next_action}, context=ctx) + + return unknown_mails + + def get_followup_table_html(self, cr, uid, ids, context = None): + """ Build the html tables to be included in emails send to partners, + when reminding them their overdue invoices. + :param ids: [id] of the partner for whom we are building the tables + :rtype: string + """ + from report import account_followup_print + if not len(ids) == 1: + raise AssertionError + if context is None: + context = {} + partner = self.browse(cr, uid, ids[0], context=context) + context = dict(context, lang=partner.lang) + followup_table = '' + company = partner.unreconciled_aml_ids and self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id + current_date = fields.date.context_today(self, cr, uid, context=context) + rml_parse = account_followup_print.report_rappel(cr, uid, 'followup_rml_parser') + final_res = rml_parse._lines_get_with_partner(partner, company.id) + for currency_dict in final_res: + currency = currency_dict.get('line', [{'currency_id': company.currency_id}])[0]['currency_id'] + followup_table += '\n \n \n \n \n \n \n \n \n \n ' + total = 0 + for aml in currency_dict['line']: + block = aml['blocked'] and 'X' or ' ' + total += aml['balance'] + strbegin = '' + date = aml['date_maturity'] or aml['date'] + if date <= current_date and aml['balance'] > 0: + strbegin = '' + followup_table += '' + strbegin + str(aml['date']) + strend + strbegin + aml['name'] + strend + strbegin + aml['ref'] + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + '' + + total = rml_parse.formatLang(total, dp='Account', currency_obj=currency) + followup_table += '\n
' + _('Invoice Date') + '' + _('Description') + '' + _('Reference') + '' + _('Due Date') + '' + _('Amount') + ' (%s)' % currency.symbol + '' + _('Lit.') + '
' + strend = '' + strend = '
\n
' + _('Amount due') + ' : %s
' % total + + return followup_table + + def write(self, cr, uid, ids, vals, context = None): + if vals.get('payment_responsible_id', False): + for part in self.browse(cr, uid, ids, context=context): + if part.payment_responsible_id != vals['payment_responsible_id']: + responsible_partner_id = self.pool.get('res.users').browse(cr, uid, vals['payment_responsible_id'], context=context).partner_id.id + self.pool.get('mail.thread').message_post(cr, uid, 0, body=_('You became responsible to do the next action for the payment follow-up of') + " " + part.name + ' ', type='comment', subtype='mail.mt_comment', context=context, model='res.partner', res_id=part.id, partner_ids=[responsible_partner_id]) + + return super(res_partner, self).write(cr, uid, ids, vals, context=context) + + def action_done(self, cr, uid, ids, context = None): + return self.write(cr, uid, ids, {'payment_next_action_date': False, + 'payment_next_action': '', + 'payment_responsible_id': False}, context=context) + + def do_button_print(self, cr, uid, ids, context = None): + if not len(ids) == 1: + raise AssertionError + company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + if not self.pool.get('account.move.line').search(cr, uid, [('partner_id', '=', ids[0]), + ('account_id.type', '=', 'receivable'), + ('reconcile_id', '=', False), + ('state', '!=', 'draft'), + ('company_id', '=', company_id)], context=context): + raise osv.except_osv(_('Error!'), _('The partner does not have any accounting entries to print in the overdue report for the current company.')) + self.message_post(cr, uid, [ids[0]], body=_('Printed overdue payments report'), context=context) + wizard_partner_ids = [ids[0] * 10000 + company_id] + followup_ids = self.pool.get('account_followup.followup').search(cr, uid, [('company_id', '=', company_id)], context=context) + raise followup_ids or osv.except_osv(_('Error!'), _('There is no followup plan defined for the current company.')) + data = {'date': fields.date.today(), + 'followup_id': followup_ids[0]} + return self.do_partner_print(cr, uid, wizard_partner_ids, data, context=context) + + def _get_amounts_and_date(self, cr, uid, ids, name, arg, context = None): + """ + Function that computes values for the followup functional fields. Note that 'payment_amount_due' + is similar to 'credit' field on res.partner except it filters on user's company. + """ + res = {} + company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id + current_date = fields.date.context_today(self, cr, uid, context=context) + for partner in self.browse(cr, uid, ids, context=context): + worst_due_date = False + amount_due = amount_overdue = 0.0 + for aml in partner.unreconciled_aml_ids: + if aml.company_id == company: + date_maturity = aml.date_maturity or aml.date + if not worst_due_date or date_maturity < worst_due_date: + worst_due_date = date_maturity + amount_due += aml.result + if date_maturity <= current_date: + amount_overdue += aml.result + + res[partner.id] = {'payment_amount_due': amount_due, + 'payment_amount_overdue': amount_overdue, + 'payment_earliest_due_date': worst_due_date} + + return res + + def _get_followup_overdue_query(self, cr, uid, args, overdue_only = False, context = None): + """ + This function is used to build the query and arguments to use when making a search on functional fields + * payment_amount_due + * payment_amount_overdue + Basically, the query is exactly the same except that for overdue there is an extra clause in the WHERE. + + :param args: arguments given to the search in the usual domain notation (list of tuples) + :param overdue_only: option to add the extra argument to filter on overdue accounting entries or not + :returns: a tuple with + * the query to execute as first element + * the arguments for the execution of this query + :rtype: (string, []) + """ + company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + having_where_clause = ' AND '.join(map(lambda x: '(SUM(bal2) %s %%s)' % x[1], args)) + having_values = [ x[2] for x in args ] + query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + overdue_only_str = overdue_only and 'AND date_maturity <= NOW()' or '' + return ("SELECT pid AS partner_id, SUM(bal2) FROM\n (SELECT CASE WHEN bal IS NOT NULL THEN bal\n ELSE 0.0 END AS bal2, p.id as pid FROM\n (SELECT (debit-credit) AS bal, partner_id\n FROM account_move_line l\n WHERE account_id IN\n (SELECT id FROM account_account\n WHERE type='receivable' AND active)\n " + overdue_only_str + '\n AND reconcile_id IS NULL\n AND company_id = %s\n AND ' + query + ') AS l\n RIGHT JOIN res_partner p\n ON p.id = partner_id ) AS pl\n GROUP BY pid HAVING ' + having_where_clause, [company_id] + having_values) + + def _payment_overdue_search(self, cr, uid, obj, name, args, context = None): + if not args: + return [] + query, query_args = self._get_followup_overdue_query(cr, uid, args, overdue_only=True, context=context) + cr.execute(query, query_args) + res = cr.fetchall() + if not res: + return [('id', '=', '0')] + return [('id', 'in', [ x[0] for x in res ])] + + def _payment_earliest_date_search(self, cr, uid, obj, name, args, context = None): + if not args: + return [] + company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + having_where_clause = ' AND '.join(map(lambda x: '(MIN(l.date_maturity) %s %%s)' % x[1], args)) + having_values = [ x[2] for x in args ] + query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + cr.execute("SELECT partner_id FROM account_move_line l WHERE account_id IN (SELECT id FROM account_account WHERE type='receivable' AND active) AND l.company_id = %s AND reconcile_id IS NULL AND " + query + ' AND partner_id IS NOT NULL GROUP BY partner_id HAVING ' + having_where_clause, [company_id] + having_values) + res = cr.fetchall() + if not res: + return [('id', '=', '0')] + return [('id', 'in', [ x[0] for x in res ])] + + def _payment_due_search(self, cr, uid, obj, name, args, context = None): + if not args: + return [] + query, query_args = self._get_followup_overdue_query(cr, uid, args, overdue_only=False, context=context) + cr.execute(query, query_args) + res = cr.fetchall() + if not res: + return [('id', '=', '0')] + return [('id', 'in', [ x[0] for x in res ])] + + _inherit = 'res.partner' + _columns = {'payment_responsible_id': fields.many2one('res.users', ondelete='set null', string='Follow-up Responsible', help='Optionally you can assign a user to this field, which will make him responsible for the action.', track_visibility='onchange'), + 'payment_note': fields.text('Customer Payment Promise', help='Payment Note', track_visibility='onchange'), + 'payment_next_action': fields.text('Next Action', help='This is the next action to be taken. It will automatically be set when the partner gets a follow-up level that requires a manual action. ', track_visibility='onchange'), + 'payment_next_action_date': fields.date('Next Action Date', help='This is when the manual follow-up is needed. The date will be set to the current date when the partner gets a follow-up level that requires a manual action. Can be practical to set manually e.g. to see if he keeps his promises.'), + 'unreconciled_aml_ids': fields.one2many('account.move.line', 'partner_id', domain=['&', + ('reconcile_id', '=', False), + '&', + ('account_id.active', '=', True), + '&', + ('account_id.type', '=', 'receivable'), + ('state', '!=', 'draft')]), + 'latest_followup_date': fields.function(_get_latest, method=True, type='date', string='Latest Follow-up Date', help='Latest date that the follow-up level of the partner was changed', store=False, multi='latest'), + 'latest_followup_level_id': fields.function(_get_latest, method=True, type='many2one', relation='account_followup.followup.line', string='Latest Follow-up Level', help='The maximum follow-up level', store=False, multi='latest'), + 'latest_followup_level_id_without_lit': fields.function(_get_latest, method=True, type='many2one', relation='account_followup.followup.line', string='Latest Follow-up Level without litigation', help='The maximum follow-up level without taking into account the account move lines with litigation', store=False, multi='latest'), + 'payment_amount_due': fields.function(_get_amounts_and_date, type='float', string='Amount Due', store=False, multi='followup', fnct_search=_payment_due_search), + 'payment_amount_overdue': fields.function(_get_amounts_and_date, type='float', string='Amount Overdue', store=False, multi='followup', fnct_search=_payment_overdue_search), + 'payment_earliest_due_date': fields.function(_get_amounts_and_date, type='date', string='Worst Due Date', multi='followup', fnct_search=_payment_earliest_date_search)} \ No newline at end of file diff --git a/addons/account_followup/account_followup_data.xml b/addons/account_followup/account_followup_data.xml index 21c638fa513d6..ec18e0d2b0c58 100644 --- a/addons/account_followup/account_followup_data.xml +++ b/addons/account_followup/account_followup_data.xml @@ -6,9 +6,9 @@ First polite payment follow-up reminder email - ${(user.email or '')|safe} + ${user.email or ''} ${user.company_id.name} Payment Reminder - ${object.email|safe} + ${object.email} ${object.lang} @@ -45,9 +45,9 @@ ${object.get_followup_table_html() | safe} A bit urging second payment follow-up reminder email - ${(user.email or '')|safe} + ${user.email or ''} ${user.company_id.name} Payment Reminder - ${object.email|safe} + ${object.email} ${object.lang} @@ -85,9 +85,9 @@ ${object.get_followup_table_html() | safe} Urging payment follow-up reminder email - ${(user.email or '')|safe} + ${user.email or ''} ${user.company_id.name} Payment Reminder - ${object.email|safe} + ${object.email} ${object.lang} @@ -122,9 +122,9 @@ ${object.get_followup_table_html() | safe} Default payment follow-up reminder e-mail - ${(user.email or '')|safe} + ${user.email or ''} ${user.company_id.name} Payment Reminder - ${object.email|safe} + ${object.email} ${object.lang} @@ -162,7 +162,7 @@ ${object.get_followup_table_html() | safe} 0 15 - True + True Dear %(partner_name)s, diff --git a/addons/account_followup/images/follow_ups.jpeg b/addons/account_followup/images/follow_ups.jpeg new file mode 100644 index 0000000000000..dfa748cd588aa Binary files /dev/null and b/addons/account_followup/images/follow_ups.jpeg differ diff --git a/addons/account_followup/images/send_followups.jpeg b/addons/account_followup/images/send_followups.jpeg new file mode 100644 index 0000000000000..16c6c1314688b Binary files /dev/null and b/addons/account_followup/images/send_followups.jpeg differ diff --git a/addons/account_followup/report/account_followup_print.py b/addons/account_followup/report/account_followup_print.py index bfec7f2f52d0a..d970a6bbdd663 100644 --- a/addons/account_followup/report/account_followup_print.py +++ b/addons/account_followup/report/account_followup_print.py @@ -1,123 +1,111 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time -from collections import defaultdict -from openerp.osv import fields - -from openerp import pooler -from openerp.report import report_sxw - -class report_rappel(report_sxw.rml_parse): - _name = "account_followup.report.rappel" - - def __init__(self, cr, uid, name, context=None): - super(report_rappel, self).__init__(cr, uid, name, context=context) - self.localcontext.update({ - 'time': time, - 'ids_to_objects': self._ids_to_objects, - 'getLines': self._lines_get, - 'get_text': self._get_text - }) - - def _ids_to_objects(self, ids): - pool = pooler.get_pool(self.cr.dbname) - all_lines = [] - for line in pool.get('account_followup.stat.by.partner').browse(self.cr, self.uid, ids): - if line not in all_lines: - all_lines.append(line) - return all_lines - - def _lines_get(self, stat_by_partner_line): - return self._lines_get_with_partner(stat_by_partner_line.partner_id, stat_by_partner_line.company_id.id) - - def _lines_get_with_partner(self, partner, company_id): - pool = pooler.get_pool(self.cr.dbname) - moveline_obj = pool.get('account.move.line') - moveline_ids = moveline_obj.search(self.cr, self.uid, [ - ('partner_id', '=', partner.id), - ('account_id.type', '=', 'receivable'), - ('reconcile_id', '=', False), - ('state', '!=', 'draft'), - ('company_id', '=', company_id), - '|', ('date_maturity', '=', False), ('date_maturity', '<=', fields.date.context_today(self, self.cr, self.uid)), - ]) - - # lines_per_currency = {currency: [line data, ...], ...} - lines_per_currency = defaultdict(list) - for line in moveline_obj.browse(self.cr, self.uid, moveline_ids): - currency = line.currency_id or line.company_id.currency_id - line_data = { - 'name': line.move_id.name, - 'ref': line.ref, - 'date': line.date, - 'date_maturity': line.date_maturity, - 'balance': line.amount_currency if currency != line.company_id.currency_id else line.debit - line.credit, - 'blocked': line.blocked, - 'currency_id': currency, - } - lines_per_currency[currency].append(line_data) - - return [{'line': lines} for lines in lines_per_currency.values()] - - def _get_text(self, stat_line, followup_id, context=None): - if context is None: - context = {} - context.update({'lang': stat_line.partner_id.lang}) - fp_obj = pooler.get_pool(self.cr.dbname).get('account_followup.followup') - fp_line = fp_obj.browse(self.cr, self.uid, followup_id, context=context).followup_line - if not fp_line: - raise osv.except_osv(_('Error!'),_("The followup plan defined for the current company does not have any followup action.")) - #the default text will be the first fp_line in the sequence with a description. - default_text = '' - li_delay = [] - for line in fp_line: - if not default_text and line.description: - default_text = line.description - li_delay.append(line.delay) - li_delay.sort(reverse=True) - a = {} - #look into the lines of the partner that already have a followup level, and take the description of the higher level for which it is available - partner_line_ids = pooler.get_pool(self.cr.dbname).get('account.move.line').search(self.cr, self.uid, [('partner_id','=',stat_line.partner_id.id),('reconcile_id','=',False),('company_id','=',stat_line.company_id.id),('blocked','=',False),('state','!=','draft'),('debit','!=',False),('account_id.type','=','receivable'),('followup_line_id','!=',False)]) - partner_max_delay = 0 - partner_max_text = '' - for i in pooler.get_pool(self.cr.dbname).get('account.move.line').browse(self.cr, self.uid, partner_line_ids, context=context): - if i.followup_line_id.delay > partner_max_delay and i.followup_line_id.description: - partner_max_delay = i.followup_line_id.delay - partner_max_text = i.followup_line_id.description - text = partner_max_delay and partner_max_text or default_text - if text: - lang_obj = self.pool['res.lang'] - lang_ids = lang_obj.search(self.cr, self.uid, [('code', '=', stat_line.partner_id.lang)], context=context) - date_format = lang_ids and lang_obj.browse(self.cr, self.uid, lang_ids[0], context=context).date_format or '%Y-%m-%d' - text = text % { - 'partner_name': stat_line.partner_id.name, - 'date': time.strftime(date_format), - 'company_name': stat_line.company_id.name, - 'user_signature': pooler.get_pool(self.cr.dbname).get('res.users').browse(self.cr, self.uid, self.uid, context).signature or '', - } - return text - -report_sxw.report_sxw('report.account_followup.followup.print', - 'account_followup.stat.by.partner', 'addons/account_followup/report/account_followup_print.rml', - parser=report_rappel) - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from collections import defaultdict +from openerp import pooler +from openerp.report import report_sxw + +class report_rappel(report_sxw.rml_parse): + _name = 'account_followup.report.rappel' + + def __init__(self, cr, uid, name, context = None): + super(report_rappel, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time, + 'ids_to_objects': self._ids_to_objects, + 'getLines': self._lines_get, + 'get_text': self._get_text}) + + def _ids_to_objects(self, ids): + pool = pooler.get_pool(self.cr.dbname) + all_lines = [] + for line in pool.get('account_followup.stat.by.partner').browse(self.cr, self.uid, ids): + if line not in all_lines: + all_lines.append(line) + + return all_lines + + def _lines_get(self, stat_by_partner_line): + return self._lines_get_with_partner(stat_by_partner_line.partner_id, stat_by_partner_line.company_id.id) + + def _lines_get_with_partner(self, partner, company_id): + pool = pooler.get_pool(self.cr.dbname) + moveline_obj = pool.get('account.move.line') + moveline_ids = moveline_obj.search(self.cr, self.uid, [('partner_id', '=', partner.id), + ('account_id.type', '=', 'receivable'), + ('reconcile_id', '=', False), + ('state', '!=', 'draft'), + ('company_id', '=', company_id)]) + lines_per_currency = defaultdict(list) + for line in moveline_obj.browse(self.cr, self.uid, moveline_ids): + currency = line.currency_id or line.company_id.currency_id + line_data = {'name': line.move_id.name, + 'ref': line.ref, + 'date': line.date, + 'date_maturity': line.date_maturity, + 'balance': line.amount_currency if currency != line.company_id.currency_id else line.debit - line.credit, + 'blocked': line.blocked, + 'currency_id': currency} + lines_per_currency[currency].append(line_data) + + return [ {'line': lines} for lines in lines_per_currency.values() ] + + def _get_text(self, stat_line, followup_id, context = None): + if context is None: + context = {} + context.update({'lang': stat_line.partner_id.lang}) + fp_obj = pooler.get_pool(self.cr.dbname).get('account_followup.followup') + fp_line = fp_obj.browse(self.cr, self.uid, followup_id, context=context).followup_line + if not fp_line: + raise osv.except_osv(_('Error!'), _('The followup plan defined for the current company does not have any followup action.')) + default_text = '' + li_delay = [] + for line in fp_line: + if not default_text and line.description: + default_text = line.description + li_delay.append(line.delay) + + li_delay.sort(reverse=True) + a = {} + partner_line_ids = pooler.get_pool(self.cr.dbname).get('account.move.line').search(self.cr, self.uid, [('partner_id', '=', stat_line.partner_id.id), + ('reconcile_id', '=', False), + ('company_id', '=', stat_line.company_id.id), + ('blocked', '=', False), + ('state', '!=', 'draft'), + ('debit', '!=', False), + ('account_id.type', '=', 'receivable'), + ('followup_line_id', '!=', False)]) + partner_max_delay = 0 + partner_max_text = '' + for i in pooler.get_pool(self.cr.dbname).get('account.move.line').browse(self.cr, self.uid, partner_line_ids, context=context): + if i.followup_line_id.delay > partner_max_delay and i.followup_line_id.description: + partner_max_delay = i.followup_line_id.delay + partner_max_text = i.followup_line_id.description + + text = partner_max_delay and partner_max_text or default_text + if text: + text = text % {'partner_name': stat_line.partner_id.name, + 'date': time.strftime('%Y-%m-%d'), + 'company_name': stat_line.company_id.name, + 'user_signature': pooler.get_pool(self.cr.dbname).get('res.users').browse(self.cr, self.uid, self.uid, context).signature or ''} + return text + + +report_sxw.report_sxw('report.account_followup.followup.print', 'account_followup.stat.by.partner', 'addons/account_followup/report/account_followup_print.rml', parser=report_rappel) \ No newline at end of file diff --git a/addons/account_followup/report/account_followup_print.sxw b/addons/account_followup/report/account_followup_print.sxw index 6e2da047a7334..4379aa5abce3b 100644 Binary files a/addons/account_followup/report/account_followup_print.sxw and b/addons/account_followup/report/account_followup_print.sxw differ diff --git a/addons/account_followup/report/account_followup_report.py b/addons/account_followup/report/account_followup_report.py index 9ca8d3c2332ac..5c43e0790ad0b 100644 --- a/addons/account_followup/report/account_followup_report.py +++ b/addons/account_followup/report/account_followup_report.py @@ -1,96 +1,67 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -from openerp import tools - -class account_followup_stat(osv.osv): - _name = "account_followup.stat" - _description = "Follow-up Statistics" - _rec_name = 'partner_id' - _auto = False - _columns = { - 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), - 'date_move':fields.date('First move', readonly=True), - 'date_move_last':fields.date('Last move', readonly=True), - 'date_followup':fields.date('Latest followup', readonly=True), - 'followup_id': fields.many2one('account_followup.followup.line', - 'Follow Ups', readonly=True, ondelete="cascade"), - 'balance':fields.float('Balance', readonly=True), - 'debit':fields.float('Debit', readonly=True), - 'credit':fields.float('Credit', readonly=True), - 'company_id': fields.many2one('res.company', 'Company', readonly=True), - 'blocked': fields.boolean('Blocked', readonly=True), - 'period_id': fields.many2one('account.period', 'Period', readonly=True), - } - _order = 'date_move' - - def search(self, cr, uid, args, offset=0, limit=None, order=None, - context=None, count=False): - for arg in args: - if arg[0] == 'period_id' and arg[2] == 'current_year': - current_year = self.pool.get('account.fiscalyear').find(cr, uid) - ids = self.pool.get('account.fiscalyear').read(cr, uid, [current_year], ['period_ids'])[0]['period_ids'] - args.append(['period_id','in',ids]) - args.remove(arg) - return super(account_followup_stat, self).search(cr, uid, args=args, offset=offset, limit=limit, order=order, - context=context, count=count) - - def read_group(self, cr, uid, domain, *args, **kwargs): - for arg in domain: - if arg[0] == 'period_id' and arg[2] == 'current_year': - current_year = self.pool.get('account.fiscalyear').find(cr, uid) - ids = self.pool.get('account.fiscalyear').read(cr, uid, [current_year], ['period_ids'])[0]['period_ids'] - domain.append(['period_id','in',ids]) - domain.remove(arg) - return super(account_followup_stat, self).read_group(cr, uid, domain, *args, **kwargs) - - def init(self, cr): - tools.drop_view_if_exists(cr, 'account_followup_stat') - cr.execute(""" - create or replace view account_followup_stat as ( - SELECT - l.id as id, - l.partner_id AS partner_id, - min(l.date) AS date_move, - max(l.date) AS date_move_last, - max(l.followup_date) AS date_followup, - max(l.followup_line_id) AS followup_id, - sum(l.debit) AS debit, - sum(l.credit) AS credit, - sum(l.debit - l.credit) AS balance, - l.company_id AS company_id, - l.blocked as blocked, - l.period_id AS period_id - FROM - account_move_line l - LEFT JOIN account_account a ON (l.account_id = a.id) - WHERE - a.active AND - a.type = 'receivable' AND - l.reconcile_id is NULL AND - l.partner_id IS NOT NULL - GROUP BY - l.id, l.partner_id, l.company_id, l.blocked, l.period_id - )""") -account_followup_stat() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +from openerp import tools + +class account_followup_stat(osv.osv): + _name = 'account_followup.stat' + _description = 'Follow-up Statistics' + _rec_name = 'partner_id' + _auto = False + _columns = {'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), + 'date_move': fields.date('First move', readonly=True), + 'date_move_last': fields.date('Last move', readonly=True), + 'date_followup': fields.date('Latest followup', readonly=True), + 'followup_id': fields.many2one('account_followup.followup.line', 'Follow Ups', readonly=True, ondelete='cascade'), + 'balance': fields.float('Balance', readonly=True), + 'debit': fields.float('Debit', readonly=True), + 'credit': fields.float('Credit', readonly=True), + 'company_id': fields.many2one('res.company', 'Company', readonly=True), + 'blocked': fields.boolean('Blocked', readonly=True), + 'period_id': fields.many2one('account.period', 'Period', readonly=True)} + _order = 'date_move' + + def search(self, cr, uid, args, offset = 0, limit = None, order = None, context = None, count = False): + for arg in args: + if arg[0] == 'period_id' and arg[2] == 'current_year': + current_year = self.pool.get('account.fiscalyear').find(cr, uid) + ids = self.pool.get('account.fiscalyear').read(cr, uid, [current_year], ['period_ids'])[0]['period_ids'] + args.append(['period_id', 'in', ids]) + args.remove(arg) + + return super(account_followup_stat, self).search(cr, uid, args=args, offset=offset, limit=limit, order=order, context=context, count=count) + + def read_group(self, cr, uid, domain, *args, **kwargs): + for arg in domain: + if arg[0] == 'period_id' and arg[2] == 'current_year': + current_year = self.pool.get('account.fiscalyear').find(cr, uid) + ids = self.pool.get('account.fiscalyear').read(cr, uid, [current_year], ['period_ids'])[0]['period_ids'] + domain.append(['period_id', 'in', ids]) + domain.remove(arg) + + return super(account_followup_stat, self).read_group(cr, uid, domain, *args, **kwargs) + + def init(self, cr): + tools.drop_view_if_exists(cr, 'account_followup_stat') + cr.execute("\n create or replace view account_followup_stat as (\n SELECT\n l.id as id,\n l.partner_id AS partner_id,\n min(l.date) AS date_move,\n max(l.date) AS date_move_last,\n max(l.followup_date) AS date_followup,\n max(l.followup_line_id) AS followup_id,\n sum(l.debit) AS debit,\n sum(l.credit) AS credit,\n sum(l.debit - l.credit) AS balance,\n l.company_id AS company_id,\n l.blocked as blocked,\n l.period_id AS period_id\n FROM\n account_move_line l\n LEFT JOIN account_account a ON (l.account_id = a.id)\n WHERE\n a.active AND\n a.type = 'receivable' AND\n l.reconcile_id is NULL AND\n l.partner_id IS NOT NULL\n GROUP BY\n l.id, l.partner_id, l.company_id, l.blocked, l.period_id\n )") + + +account_followup_stat() \ No newline at end of file diff --git a/addons/account_followup/wizard/account_followup_print.py b/addons/account_followup/wizard/account_followup_print.py index 52593cc747002..e9aa4dceac723 100644 --- a/addons/account_followup/wizard/account_followup_print.py +++ b/addons/account_followup/wizard/account_followup_print.py @@ -1,320 +1,249 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import datetime -import time - -from openerp import tools -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -class account_followup_stat_by_partner(osv.osv): - _name = "account_followup.stat.by.partner" - _description = "Follow-up Statistics by Partner" - _rec_name = 'partner_id' - _auto = False - _columns = { - 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), - 'date_move':fields.date('First move', readonly=True), - 'date_move_last':fields.date('Last move', readonly=True), - 'date_followup':fields.date('Latest follow-up', readonly=True), - 'max_followup_id': fields.many2one('account_followup.followup.line', - 'Max Follow Up Level', readonly=True, ondelete="cascade"), - 'balance':fields.float('Balance', readonly=True), - 'company_id': fields.many2one('res.company', 'Company', readonly=True), - } - - def init(self, cr): - tools.drop_view_if_exists(cr, 'account_followup_stat_by_partner') - # Here we don't have other choice but to create a virtual ID based on the concatenation - # of the partner_id and the company_id, because if a partner is shared between 2 companies, - # we want to see 2 lines for him in this table. It means that both company should be able - # to send him follow-ups separately . An assumption that the number of companies will not - # reach 10 000 records is made, what should be enough for a time. - cr.execute(""" - create or replace view account_followup_stat_by_partner as ( - SELECT - l.partner_id * 10000 + l.company_id as id, - l.partner_id AS partner_id, - min(l.date) AS date_move, - max(l.date) AS date_move_last, - max(l.followup_date) AS date_followup, - max(l.followup_line_id) AS max_followup_id, - sum(l.debit - l.credit) AS balance, - l.company_id as company_id - FROM - account_move_line l - LEFT JOIN account_account a ON (l.account_id = a.id) - WHERE - a.active AND - a.type = 'receivable' AND - l.reconcile_id is NULL AND - l.partner_id IS NOT NULL AND - (l.blocked = False) - GROUP BY - l.partner_id, l.company_id - )""") #Blocked is to take into account litigation -account_followup_stat_by_partner() - - -class account_followup_sending_results(osv.osv_memory): - - def do_report(self, cr, uid, ids, context=None): - if context is None: - context = {} - return context.get('report_data') - - def do_done(self, cr, uid, ids, context=None): - return {} - - def _get_description(self, cr, uid, context=None): - if context is None: - context = {} - return context.get('description') - - def _get_need_printing(self, cr, uid, context=None): - if context is None: - context = {} - return context.get('needprinting') - - _name = 'account_followup.sending.results' - _description = 'Results from the sending of the different letters and emails' - _columns = { - 'description': fields.text("Description", readonly=True), - 'needprinting': fields.boolean("Needs Printing") - } - _defaults = { - 'needprinting':_get_need_printing, - 'description':_get_description, - } - -account_followup_sending_results() - - -class account_followup_print(osv.osv_memory): - _name = 'account_followup.print' - _description = 'Print Follow-up & Send Mail to Customers' - _columns = { - 'date': fields.date('Follow-up Sending Date', required=True, - help="This field allow you to select a forecast date to plan your follow-ups"), - 'followup_id': fields.many2one('account_followup.followup', 'Follow-Up', required=True, readonly = True), - 'partner_ids': fields.many2many('account_followup.stat.by.partner', 'partner_stat_rel', - 'osv_memory_id', 'partner_id', 'Partners', required=True), - 'company_id':fields.related('followup_id', 'company_id', type='many2one', - relation='res.company', store=True, readonly=True), - 'email_conf': fields.boolean('Send Email Confirmation'), - 'email_subject': fields.char('Email Subject', size=64), - 'partner_lang': fields.boolean('Send Email in Partner Language', - help='Do not change message text, if you want to send email in partner language, or configure from company'), - 'email_body': fields.text('Email Body'), - 'summary': fields.text('Summary', readonly=True), - 'test_print': fields.boolean('Test Print', - help='Check if you want to print follow-ups without changing follow-up level.'), - } - - def _get_followup(self, cr, uid, context=None): - if context is None: - context = {} - if context.get('active_model', 'ir.ui.menu') == 'account_followup.followup': - return context.get('active_id', False) - company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id - followp_id = self.pool.get('account_followup.followup').search(cr, uid, [('company_id', '=', company_id)], context=context) - return followp_id and followp_id[0] or False - - def process_partners(self, cr, uid, partner_ids, data, context=None): - partner_obj = self.pool.get('res.partner') - partner_ids_to_print = [] - nbmanuals = 0 - manuals = {} - nbmails = 0 - nbunknownmails = 0 - nbprints = 0 - resulttext = " " - for partner in self.pool.get('account_followup.stat.by.partner').browse(cr, uid, partner_ids, context=context): - if partner.max_followup_id.manual_action: - partner_obj.do_partner_manual_action(cr, uid, [partner.partner_id.id], context=context) - nbmanuals = nbmanuals + 1 - key = partner.partner_id.payment_responsible_id.name or _("Anybody") - if not key in manuals.keys(): - manuals[key]= 1 - else: - manuals[key] = manuals[key] + 1 - if partner.max_followup_id.send_email: - nbunknownmails += partner_obj.do_partner_mail(cr, uid, [partner.partner_id.id], context=context) - nbmails += 1 - if partner.max_followup_id.send_letter: - partner_ids_to_print.append(partner.id) - nbprints += 1 - message = "%s %s %s" % (_("Follow-up letter of "), partner.partner_id.latest_followup_level_id_without_lit.name, _(" will be sent")) - partner_obj.message_post(cr, uid, [partner.partner_id.id], body=message, context=context) - if nbunknownmails == 0: - resulttext += str(nbmails) + _(" email(s) sent") - else: - resulttext += str(nbmails) + _(" email(s) should have been sent, but ") + str(nbunknownmails) + _(" had unknown email address(es)") + "\n
" - resulttext += "
" + str(nbprints) + _(" letter(s) in report") + " \n
" + str(nbmanuals) + _(" manual action(s) assigned:") - needprinting = False - if nbprints > 0: - needprinting = True - resulttext += "

" - for item in manuals: - resulttext = resulttext + "

  • " + item + ":" + str(manuals[item]) + "\n
  • " - resulttext += "

    " - result = {} - action = partner_obj.do_partner_print(cr, uid, partner_ids_to_print, data, context=context) - result['needprinting'] = needprinting - result['resulttext'] = resulttext - result['action'] = action or {} - return result - - def do_update_followup_level(self, cr, uid, to_update, partner_list, date, context=None): - #update the follow-up level on account.move.line - for id in to_update.keys(): - if to_update[id]['partner_id'] in partner_list: - self.pool.get('account.move.line').write(cr, uid, [int(id)], {'followup_line_id': to_update[id]['level'], - 'followup_date': date}) - - def clear_manual_actions(self, cr, uid, partner_list, context=None): - # Partnerlist is list to exclude - # Will clear the actions of partners that have no due payments anymore - partner_list_ids = [partner.partner_id.id for partner in self.pool.get('account_followup.stat.by.partner').browse(cr, uid, partner_list, context=context)] - ids = self.pool.get('res.partner').search(cr, uid, ['&', ('id', 'not in', partner_list_ids), '|', - ('payment_responsible_id', '!=', False), - ('payment_next_action_date', '!=', False)], context=context) - - partners_to_clear = [] - for part in self.pool.get('res.partner').browse(cr, uid, ids, context=context): - if not part.unreconciled_aml_ids: - partners_to_clear.append(part.id) - self.pool.get('res.partner').action_done(cr, uid, partners_to_clear, context=context) - return len(partners_to_clear) - - def do_process(self, cr, uid, ids, context=None): - if context is None: - context = {} - - #Get partners - tmp = self._get_partners_followp(cr, uid, ids, context=context) - partner_list = tmp['partner_ids'] - to_update = tmp['to_update'] - date = self.browse(cr, uid, ids, context=context)[0].date - data = self.read(cr, uid, ids, [], context=context)[0] - data['followup_id'] = data['followup_id'][0] - - #Update partners - self.do_update_followup_level(cr, uid, to_update, partner_list, date, context=context) - #process the partners (send mails...) - restot = self.process_partners(cr, uid, partner_list, data, context=context) - #clear the manual actions if nothing is due anymore - nbactionscleared = self.clear_manual_actions(cr, uid, partner_list, context=context) - if nbactionscleared > 0: - restot['resulttext'] = restot['resulttext'] + "
  • " + _("%s partners have no credits and as such the action is cleared") %(str(nbactionscleared)) + "
  • " - res = restot['action'] - - #return the next action - mod_obj = self.pool.get('ir.model.data') - model_data_ids = mod_obj.search(cr, uid, [('model','=','ir.ui.view'),('name','=','view_account_followup_sending_results')], context=context) - resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] - context.update({'description': restot['resulttext'], 'needprinting': restot['needprinting'], 'report_data': res}) - return { - 'name': _('Send Letters and Emails: Actions Summary'), - 'view_type': 'form', - 'context': context, - 'view_mode': 'tree,form', - 'res_model': 'account_followup.sending.results', - 'views': [(resource_id,'form')], - 'type': 'ir.actions.act_window', - 'target': 'new', - } - - def _get_msg(self, cr, uid, context=None): - return self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.follow_up_msg - - _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d'), - 'followup_id': _get_followup, - 'email_body': "", - 'email_subject': _('Invoices Reminder'), - 'partner_lang': True, - } - - def _get_partners_followp(self, cr, uid, ids, context=None): - data = {} - data = self.browse(cr, uid, ids, context=context)[0] - company_id = data.company_id.id - - cr.execute( - "SELECT l.partner_id, l.followup_line_id,l.date_maturity, l.date, l.id "\ - "FROM account_move_line AS l "\ - "LEFT JOIN account_account AS a "\ - "ON (l.account_id=a.id) "\ - "WHERE (l.reconcile_id IS NULL) "\ - "AND (a.type='receivable') "\ - "AND (l.state<>'draft') "\ - "AND (l.partner_id is NOT NULL) "\ - "AND (a.active) "\ - "AND (l.debit > 0) "\ - "AND (l.company_id = %s) " \ - "AND (l.blocked = False)" \ - "ORDER BY l.date", (company_id,)) #l.blocked added to take litigation into account and it is not necessary to change follow-up level of account move lines without debit - move_lines = cr.fetchall() - old = None - fups = {} - fup_id = 'followup_id' in context and context['followup_id'] or data.followup_id.id - date = 'date' in context and context['date'] or data.date - - current_date = datetime.date(*time.strptime(date, - '%Y-%m-%d')[:3]) - cr.execute( - "SELECT * "\ - "FROM account_followup_followup_line "\ - "WHERE followup_id=%s "\ - "ORDER BY delay", (fup_id,)) - - #Create dictionary of tuples where first element is the date to compare with the due date and second element is the id of the next level - for result in cr.dictfetchall(): - delay = datetime.timedelta(days=result['delay']) - fups[old] = (current_date - delay, result['id']) - old = result['id'] - - partner_list = [] - to_update = {} - - #Fill dictionary of accountmovelines to_update with the partners that need to be updated - for partner_id, followup_line_id, date_maturity,date, id in move_lines: - if not partner_id: - continue - if followup_line_id not in fups: - continue - stat_line_id = partner_id * 10000 + company_id - if date_maturity: - if date_maturity <= fups[followup_line_id][0].strftime('%Y-%m-%d'): - if stat_line_id not in partner_list: - partner_list.append(stat_line_id) - to_update[str(id)]= {'level': fups[followup_line_id][1], 'partner_id': stat_line_id} - elif date and date <= fups[followup_line_id][0].strftime('%Y-%m-%d'): - if stat_line_id not in partner_list: - partner_list.append(stat_line_id) - to_update[str(id)]= {'level': fups[followup_line_id][1], 'partner_id': stat_line_id} - return {'partner_ids': partner_list, 'to_update': to_update} - -account_followup_print() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import datetime +import time +from openerp import tools +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class account_followup_stat_by_partner(osv.osv): + _name = 'account_followup.stat.by.partner' + _description = 'Follow-up Statistics by Partner' + _rec_name = 'partner_id' + _auto = False + _columns = {'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), + 'date_move': fields.date('First move', readonly=True), + 'date_move_last': fields.date('Last move', readonly=True), + 'date_followup': fields.date('Latest follow-up', readonly=True), + 'max_followup_id': fields.many2one('account_followup.followup.line', 'Max Follow Up Level', readonly=True, ondelete='cascade'), + 'balance': fields.float('Balance', readonly=True), + 'company_id': fields.many2one('res.company', 'Company', readonly=True)} + + def init(self, cr): + tools.drop_view_if_exists(cr, 'account_followup_stat_by_partner') + cr.execute("\n create or replace view account_followup_stat_by_partner as (\n SELECT\n l.partner_id * 10000 + l.company_id as id,\n l.partner_id AS partner_id,\n min(l.date) AS date_move,\n max(l.date) AS date_move_last,\n max(l.followup_date) AS date_followup,\n max(l.followup_line_id) AS max_followup_id,\n sum(l.debit - l.credit) AS balance,\n l.company_id as company_id\n FROM\n account_move_line l\n LEFT JOIN account_account a ON (l.account_id = a.id)\n WHERE\n a.active AND\n a.type = 'receivable' AND\n l.reconcile_id is NULL AND\n l.partner_id IS NOT NULL AND\n (l.blocked = False)\n GROUP BY\n l.partner_id, l.company_id\n )") + + +account_followup_stat_by_partner() + +class account_followup_sending_results(osv.osv_memory): + + def do_report(self, cr, uid, ids, context = None): + if context is None: + context = {} + return context.get('report_data') + + def do_done(self, cr, uid, ids, context = None): + return {} + + def _get_description(self, cr, uid, context = None): + if context is None: + context = {} + return context.get('description') + + def _get_need_printing(self, cr, uid, context = None): + if context is None: + context = {} + return context.get('needprinting') + + _name = 'account_followup.sending.results' + _description = 'Results from the sending of the different letters and emails' + _columns = {'description': fields.text('Description', readonly=True), + 'needprinting': fields.boolean('Needs Printing')} + _defaults = {'needprinting': _get_need_printing, + 'description': _get_description} + + +account_followup_sending_results() + +class account_followup_print(osv.osv_memory): + _name = 'account_followup.print' + _description = 'Print Follow-up & Send Mail to Customers' + _columns = {'date': fields.date('Follow-up Sending Date', required=True, help='This field allow you to select a forecast date to plan your follow-ups'), + 'followup_id': fields.many2one('account_followup.followup', 'Follow-Up', required=True, readonly=True), + 'partner_ids': fields.many2many('account_followup.stat.by.partner', 'partner_stat_rel', 'osv_memory_id', 'partner_id', 'Partners', required=True), + 'company_id': fields.related('followup_id', 'company_id', type='many2one', relation='res.company', store=True, readonly=True), + 'email_conf': fields.boolean('Send Email Confirmation'), + 'email_subject': fields.char('Email Subject', size=64), + 'partner_lang': fields.boolean('Send Email in Partner Language', help='Do not change message text, if you want to send email in partner language, or configure from company'), + 'email_body': fields.text('Email Body'), + 'summary': fields.text('Summary', readonly=True), + 'test_print': fields.boolean('Test Print', help='Check if you want to print follow-ups without changing follow-up level.')} + + def _get_followup(self, cr, uid, context = None): + if context is None: + context = {} + if context.get('active_model', 'ir.ui.menu') == 'account_followup.followup': + return context.get('active_id', False) + else: + company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + followp_id = self.pool.get('account_followup.followup').search(cr, uid, [('company_id', '=', company_id)], context=context) + return followp_id and followp_id[0] or False + + def process_partners(self, cr, uid, partner_ids, data, context = None): + partner_obj = self.pool.get('res.partner') + partner_ids_to_print = [] + nbmanuals = 0 + manuals = {} + nbmails = 0 + nbunknownmails = 0 + nbprints = 0 + resulttext = ' ' + for partner in self.pool.get('account_followup.stat.by.partner').browse(cr, uid, partner_ids, context=context): + if partner.max_followup_id.manual_action: + partner_obj.do_partner_manual_action(cr, uid, [partner.partner_id.id], context=context) + nbmanuals = nbmanuals + 1 + key = partner.partner_id.payment_responsible_id.name or _('Anybody') + if key not in manuals.keys(): + manuals[key] = 1 + else: + manuals[key] = manuals[key] + 1 + if partner.max_followup_id.send_email: + nbunknownmails += partner_obj.do_partner_mail(cr, uid, [partner.partner_id.id], context=context) + nbmails += 1 + if partner.max_followup_id.send_letter: + partner_ids_to_print.append(partner.id) + nbprints += 1 + message = _('Follow-up letter of ') + ' ' + partner.partner_id.latest_followup_level_id_without_lit.name + '' + _(' will be sent') + partner_obj.message_post(cr, uid, [partner.partner_id.id], body=message, context=context) + + if nbunknownmails == 0: + resulttext += str(nbmails) + _(' email(s) sent') + else: + resulttext += str(nbmails) + _(' email(s) should have been sent, but ') + str(nbunknownmails) + _(' had unknown email address(es)') + '\n
    ' + resulttext += '
    ' + str(nbprints) + _(' letter(s) in report') + ' \n
    ' + str(nbmanuals) + _(' manual action(s) assigned:') + needprinting = False + if nbprints > 0: + needprinting = True + resulttext += '

    ' + for item in manuals: + resulttext = resulttext + '

  • ' + item + ':' + str(manuals[item]) + '\n
  • ' + + resulttext += '

    ' + result = {} + action = partner_obj.do_partner_print(cr, uid, partner_ids_to_print, data, context=context) + result['needprinting'] = needprinting + result['resulttext'] = resulttext + result['action'] = action or {} + return result + + def do_update_followup_level(self, cr, uid, to_update, partner_list, date, context = None): + for id in to_update.keys(): + if to_update[id]['partner_id'] in partner_list: + self.pool.get('account.move.line').write(cr, uid, [int(id)], {'followup_line_id': to_update[id]['level'], + 'followup_date': date}) + + def clear_manual_actions(self, cr, uid, partner_list, context = None): + partner_list_ids = [ partner.partner_id.id for partner in self.pool.get('account_followup.stat.by.partner').browse(cr, uid, partner_list, context=context) ] + ids = self.pool.get('res.partner').search(cr, uid, ['&', + ('id', 'not in', partner_list_ids), + '|', + ('payment_responsible_id', '!=', False), + ('payment_next_action_date', '!=', False)], context=context) + partners_to_clear = [] + for part in self.pool.get('res.partner').browse(cr, uid, ids, context=context): + if not part.unreconciled_aml_ids: + partners_to_clear.append(part.id) + + self.pool.get('res.partner').action_done(cr, uid, partners_to_clear, context=context) + return len(partners_to_clear) + + def do_process(self, cr, uid, ids, context = None): + if context is None: + context = {} + tmp = self._get_partners_followp(cr, uid, ids, context=context) + partner_list = tmp['partner_ids'] + to_update = tmp['to_update'] + date = self.browse(cr, uid, ids, context=context)[0].date + data = self.read(cr, uid, ids, [], context=context)[0] + data['followup_id'] = data['followup_id'][0] + self.do_update_followup_level(cr, uid, to_update, partner_list, date, context=context) + restot = self.process_partners(cr, uid, partner_list, data, context=context) + nbactionscleared = self.clear_manual_actions(cr, uid, partner_list, context=context) + if nbactionscleared > 0: + restot['resulttext'] = restot['resulttext'] + '
  • ' + _('%s partners have no credits and as such the action is cleared') % str(nbactionscleared) + '
  • ' + res = restot['action'] + mod_obj = self.pool.get('ir.model.data') + model_data_ids = mod_obj.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_account_followup_sending_results')], context=context) + resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] + context.update({'description': restot['resulttext'], + 'needprinting': restot['needprinting'], + 'report_data': res}) + return {'name': _('Send Letters and Emails: Actions Summary'), + 'view_type': 'form', + 'context': context, + 'view_mode': 'tree,form', + 'res_model': 'account_followup.sending.results', + 'views': [(resource_id, 'form')], + 'type': 'ir.actions.act_window', + 'target': 'new'} + + def _get_msg(self, cr, uid, context = None): + return self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.follow_up_msg + + _defaults = {'date': lambda *a: time.strftime('%Y-%m-%d'), + 'followup_id': _get_followup, + 'email_body': '', + 'email_subject': _('Invoices Reminder'), + 'partner_lang': True} + + def _get_partners_followp(self, cr, uid, ids, context = None): + data = {} + data = self.browse(cr, uid, ids, context=context)[0] + company_id = data.company_id.id + cr.execute("SELECT l.partner_id, l.followup_line_id,l.date_maturity, l.date, l.id FROM account_move_line AS l LEFT JOIN account_account AS a ON (l.account_id=a.id) WHERE (l.reconcile_id IS NULL) AND (a.type='receivable') AND (l.state<>'draft') AND (l.partner_id is NOT NULL) AND (a.active) AND (l.debit > 0) AND (l.company_id = %s) AND (l.blocked = False)ORDER BY l.date", (company_id,)) + move_lines = cr.fetchall() + old = None + fups = {} + fup_id = 'followup_id' in context and context['followup_id'] or data.followup_id.id + date = 'date' in context and context['date'] or data.date + current_date = datetime.date(*time.strptime(date, '%Y-%m-%d')[:3]) + cr.execute('SELECT * FROM account_followup_followup_line WHERE followup_id=%s ORDER BY delay', (fup_id,)) + for result in cr.dictfetchall(): + delay = datetime.timedelta(days=result['delay']) + fups[old] = (current_date - delay, result['id']) + old = result['id'] + + partner_list = [] + to_update = {} + for partner_id, followup_line_id, date_maturity, date, id in move_lines: + if not partner_id: + continue + if followup_line_id not in fups: + continue + stat_line_id = partner_id * 10000 + company_id + if date_maturity: + if date_maturity <= fups[followup_line_id][0].strftime('%Y-%m-%d'): + if stat_line_id not in partner_list: + partner_list.append(stat_line_id) + to_update[str(id)] = {'level': fups[followup_line_id][1], + 'partner_id': stat_line_id} + elif date and date <= fups[followup_line_id][0].strftime('%Y-%m-%d'): + if stat_line_id not in partner_list: + partner_list.append(stat_line_id) + to_update[str(id)] = {'level': fups[followup_line_id][1], + 'partner_id': stat_line_id} + + return {'partner_ids': partner_list, + 'to_update': to_update} + + +account_followup_print() \ No newline at end of file diff --git a/addons/account_payment/account_invoice.py b/addons/account_payment/account_invoice.py index 916aa07afa026..eaf8f1289704d 100644 --- a/addons/account_payment/account_invoice.py +++ b/addons/account_payment/account_invoice.py @@ -1,55 +1,45 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from datetime import datetime -from openerp.tools.translate import _ -from openerp.osv import fields, osv - -class Invoice(osv.osv): - _inherit = 'account.invoice' - - # Forbid to cancel an invoice if the related move lines have already been - # used in a payment order. The risk is that importing the payment line - # in the bank statement will result in a crash cause no more move will - # be found in the payment line - def action_cancel(self, cr, uid, ids, context=None): - payment_line_obj = self.pool.get('payment.line') - for inv in self.browse(cr, uid, ids, context=context): - pl_line_ids = [] - if inv.move_id and inv.move_id.line_id: - inv_mv_lines = [x.id for x in inv.move_id.line_id] - pl_line_ids = payment_line_obj.search(cr, uid, [('move_line_id','in',inv_mv_lines)], context=context) - if pl_line_ids: - pay_line = payment_line_obj.browse(cr, uid, pl_line_ids, context=context) - payment_order_name = ','.join(map(lambda x: x.order_id.reference, pay_line)) - raise osv.except_osv(_('Error!'), _("You cannot cancel an invoice which has already been imported in a payment order. Remove it from the following payment order : %s."%(payment_order_name))) - return super(Invoice, self).action_cancel(cr, uid, ids, context=context) - - - _columns = { - 'amount_to_pay': fields.related('residual', - type='float', string='Amount to be paid', - help='The amount which should be paid at the current date. '), - } - -Invoice() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from datetime import datetime +from openerp.tools.translate import _ +from openerp.osv import fields, osv + +class Invoice(osv.osv): + _inherit = 'account.invoice' + + def action_cancel(self, cr, uid, ids, context = None): + payment_line_obj = self.pool.get('payment.line') + for inv in self.browse(cr, uid, ids, context=context): + pl_line_ids = [] + if inv.move_id and inv.move_id.line_id: + inv_mv_lines = [ x.id for x in inv.move_id.line_id ] + pl_line_ids = payment_line_obj.search(cr, uid, [('move_line_id', 'in', inv_mv_lines)], context=context) + if pl_line_ids: + pay_line = payment_line_obj.browse(cr, uid, pl_line_ids, context=context) + payment_order_name = ','.join(map(lambda x: x.order_id.reference, pay_line)) + raise osv.except_osv(_('Error!'), _('You cannot cancel an invoice which has already been imported in a payment order. Remove it from the following payment order : %s.' % payment_order_name)) + + return super(Invoice, self).action_cancel(cr, uid, ids, context=context) + + _columns = {'amount_to_pay': fields.related('residual', type='float', string='Amount to be paid', help='The amount which should be paid at the current date. ')} + + +Invoice() \ No newline at end of file diff --git a/addons/account_payment/account_move_line.py b/addons/account_payment/account_move_line.py index 9068d8b729c3a..b4aa145d2e830 100644 --- a/addons/account_payment/account_move_line.py +++ b/addons/account_payment/account_move_line.py @@ -1,112 +1,86 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from operator import itemgetter -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -class account_move_line(osv.osv): - _inherit = "account.move.line" - - def amount_to_pay(self, cr, uid, ids, name, arg=None, context=None): - """ Return the amount still to pay regarding all the payemnt orders - (excepting cancelled orders)""" - res = {} - if not ids: - return res - for ml in self.browse(cr, uid, ids, context=context): - if ml.amount_currency: - res[ml.id] = ml.amount_residual_currency - else: - res[ml.id] = ml.amount_residual - return res - - def _to_pay_search(self, cr, uid, obj, name, args, context=None): - if not args: - return [] - line_obj = self.pool.get('account.move.line') - query = line_obj._query_get(cr, uid, context={}) - where = ' and '.join(map(lambda x: '''(SELECT - CASE WHEN l.amount_currency < 0 - THEN - l.amount_currency - ELSE l.credit - END - coalesce(sum(pl.amount_currency), 0) - FROM payment_line pl - INNER JOIN payment_order po ON (pl.order_id = po.id) - WHERE move_line_id = l.id - AND po.state != 'cancel' - ) %(operator)s %%s ''' % {'operator': x[1]}, args)) - sql_args = tuple(map(itemgetter(2), args)) - - cr.execute(('''SELECT id - FROM account_move_line l - WHERE account_id IN (select id - FROM account_account - WHERE type=%s AND active) - AND reconcile_id IS null - AND credit > 0 - AND ''' + where + ' and ' + query), ('payable',)+sql_args ) - - res = cr.fetchall() - if not res: - return [('id', '=', '0')] - return [('id', 'in', map(lambda x:x[0], res))] - - def line2bank(self, cr, uid, ids, payment_type=None, context=None): - """ - Try to return for each Ledger Posting line a corresponding bank - account according to the payment type. This work using one of - the bank of the partner defined on the invoice eventually - associated to the line. - Return the first suitable bank for the corresponding partner. - """ - payment_mode_obj = self.pool.get('payment.mode') - line2bank = {} - if not ids: - return {} - bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, - context=context) - for line in self.browse(cr, uid, ids, context=context): - line2bank[line.id] = False - if line.invoice and line.invoice.partner_bank_id: - line2bank[line.id] = line.invoice.partner_bank_id.id - elif line.partner_id: - if not line.partner_id.bank_ids: - line2bank[line.id] = False - else: - for bank in line.partner_id.bank_ids: - if bank.state in bank_type: - line2bank[line.id] = bank.id - break - if not line2bank.get(line.id) and line.partner_id.bank_ids: - line2bank[line.id] = line.partner_id.bank_ids[0].id - else: - raise osv.except_osv(_('Error!'), _('There is no partner defined on the entry line.')) - return line2bank - - _columns = { - 'amount_to_pay': fields.function(amount_to_pay, - type='float', string='Amount to pay', fnct_search=_to_pay_search), - } - -account_move_line() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from operator import itemgetter +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class account_move_line(osv.osv): + _inherit = 'account.move.line' + + def amount_to_pay(self, cr, uid, ids, name, arg = None, context = None): + """ Return the amount still to pay regarding all the payemnt orders + (excepting cancelled orders)""" + if not ids: + return {} + cr.execute("SELECT ml.id,\n CASE WHEN ml.amount_currency < 0\n THEN - ml.amount_currency\n ELSE ml.credit\n END -\n (SELECT coalesce(sum(amount_currency),0)\n FROM payment_line pl\n INNER JOIN payment_order po\n ON (pl.order_id = po.id)\n WHERE move_line_id = ml.id\n AND po.state != 'cancel') AS amount\n FROM account_move_line ml\n WHERE id IN %s", (tuple(ids),)) + r = dict(cr.fetchall()) + return r + + def _to_pay_search(self, cr, uid, obj, name, args, context = None): + if not args: + return [] + line_obj = self.pool.get('account.move.line') + query = line_obj._query_get(cr, uid, context={}) + where = ' and '.join(map(lambda x: "(SELECT\n CASE WHEN l.amount_currency < 0\n THEN - l.amount_currency\n ELSE l.credit\n END - coalesce(sum(pl.amount_currency), 0)\n FROM payment_line pl\n INNER JOIN payment_order po ON (pl.order_id = po.id)\n WHERE move_line_id = l.id\n AND po.state != 'cancel'\n ) %(operator)s %%s " % {'operator': x[1]}, args)) + sql_args = tuple(map(itemgetter(2), args)) + cr.execute('SELECT id\n FROM account_move_line l\n WHERE account_id IN (select id\n FROM account_account\n WHERE type=%s AND active)\n AND reconcile_id IS null\n AND credit > 0\n AND ' + where + ' and ' + query, ('payable',) + sql_args) + res = cr.fetchall() + if not res: + return [('id', '=', '0')] + return [('id', 'in', map(lambda x: x[0], res))] + + def line2bank(self, cr, uid, ids, payment_type = None, context = None): + """ + Try to return for each Ledger Posting line a corresponding bank + account according to the payment type. This work using one of + the bank of the partner defined on the invoice eventually + associated to the line. + Return the first suitable bank for the corresponding partner. + """ + payment_mode_obj = self.pool.get('payment.mode') + line2bank = {} + if not ids: + return {} + bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context) + for line in self.browse(cr, uid, ids, context=context): + line2bank[line.id] = False + if line.invoice and line.invoice.partner_bank_id: + line2bank[line.id] = line.invoice.partner_bank_id.id + elif line.partner_id: + if not line.partner_id.bank_ids: + line2bank[line.id] = False + else: + for bank in line.partner_id.bank_ids: + if bank.state in bank_type: + line2bank[line.id] = bank.id + break + + if line.id not in line2bank and line.partner_id.bank_ids: + line2bank[line.id] = line.partner_id.bank_ids[0].id + else: + raise osv.except_osv(_('Error!'), _('There is no partner defined on the entry line.')) + + return line2bank + + _columns = {'amount_to_pay': fields.function(amount_to_pay, type='float', string='Amount to pay', fnct_search=_to_pay_search)} + + +account_move_line() \ No newline at end of file diff --git a/addons/account_payment/account_payment.py b/addons/account_payment/account_payment.py index d67db60e59071..4781c9605ceba 100644 --- a/addons/account_payment/account_payment.py +++ b/addons/account_payment/account_payment.py @@ -1,426 +1,376 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import logging -import time - -from openerp.osv import fields, osv -from openerp import netsvc - -_logger = logging.getLogger(__name__) - -class payment_mode(osv.osv): - _name= 'payment.mode' - _description= 'Payment Mode' - _columns = { - 'name': fields.char('Name', size=64, required=True, help='Mode of Payment'), - 'bank_id': fields.many2one('res.partner.bank', "Bank account", - required=True,help='Bank Account for the Payment Mode'), - 'journal': fields.many2one('account.journal', 'Journal', required=True, - domain=[('type', 'in', ('bank','cash'))], help='Bank or Cash Journal for the Payment Mode'), - 'company_id': fields.many2one('res.company', 'Company',required=True), - 'partner_id':fields.related('company_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True,), - - } - _defaults = { - 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id - } - - def suitable_bank_types(self, cr, uid, payment_code=None, context=None): - """Return the codes of the bank type that are suitable - for the given payment type code""" - if not payment_code: - return [] - cr.execute(""" SELECT pb.state - FROM res_partner_bank pb - JOIN payment_mode pm ON (pm.bank_id = pb.id) - WHERE pm.id = %s """, [payment_code]) - return [x[0] for x in cr.fetchall()] - - def onchange_company_id (self, cr, uid, ids, company_id=False, context=None): - result = {} - if company_id: - partner_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).partner_id.id - result['partner_id'] = partner_id - return {'value': result} - - -payment_mode() - -class payment_order(osv.osv): - _name = 'payment.order' - _description = 'Payment Order' - _rec_name = 'reference' - _order = 'id desc' - - #dead code - def get_wizard(self, type): - _logger.warning("No wizard found for the payment type '%s'.", type) - return None - - def _total(self, cursor, user, ids, name, args, context=None): - if not ids: - return {} - res = {} - for order in self.browse(cursor, user, ids, context=context): - if order.line_ids: - res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0) - else: - res[order.id] = 0.0 - return res - - _columns = { - 'date_scheduled': fields.date('Scheduled Date', states={'done':[('readonly', True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'), - 'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}), - 'mode': fields.many2one('payment.mode', 'Payment Mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'), - 'state': fields.selection([ - ('draft', 'Draft'), - ('cancel', 'Cancelled'), - ('open', 'Confirmed'), - ('done', 'Done')], 'Status', select=True, - help='When an order is placed the status is \'Draft\'.\n Once the bank is confirmed the status is set to \'Confirmed\'.\n Then the order is paid the status is \'Done\'.'), - 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}), - 'total': fields.function(_total, string="Total", type='float'), - 'user_id': fields.many2one('res.users', 'Responsible', required=True, states={'done': [('readonly', True)]}), - 'date_prefered': fields.selection([ - ('now', 'Directly'), - ('due', 'Due date'), - ('fixed', 'Fixed date') - ], "Preferred Date", change_default=True, required=True, states={'done': [('readonly', True)]}, help="Choose an option for the Payment Order:'Fixed' stands for a date specified by you.'Directly' stands for the direct execution.'Due date' stands for the scheduled date of execution."), - 'date_created': fields.date('Creation Date', readonly=True), - 'date_done': fields.date('Execution Date', readonly=True), - 'company_id': fields.related('mode', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), - } - - _defaults = { - 'user_id': lambda self,cr,uid,context: uid, - 'state': 'draft', - 'date_prefered': 'due', - 'date_created': lambda *a: time.strftime('%Y-%m-%d'), - 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'), - } - - def set_to_draft(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state': 'draft'}) - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_create(uid, 'payment.order', id, cr) - return True - - def action_open(self, cr, uid, ids, *args): - ir_seq_obj = self.pool.get('ir.sequence') - - for order in self.read(cr, uid, ids, ['reference']): - if not order['reference']: - reference = ir_seq_obj.get(cr, uid, 'payment.order') - self.write(cr, uid, order['id'], {'reference':reference}) - return True - - def set_done(self, cr, uid, ids, *args): - wf_service = netsvc.LocalService("workflow") - self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')}) - wf_service.trg_validate(uid, 'payment.order', ids[0], 'done', cr) - return True - - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - default.update({ - 'state': 'draft', - 'line_ids': [], - 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order') - }) - return super(payment_order, self).copy(cr, uid, id, default, context=context) - - def write(self, cr, uid, ids, vals, context=None): - if context is None: - context = {} - payment_line_obj = self.pool.get('payment.line') - payment_line_ids = [] - - if (vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False)) or vals.get('date_scheduled', False): - for order in self.browse(cr, uid, ids, context=context): - for line in order.line_ids: - payment_line_ids.append(line.id) - payment_line_obj.write(cr, uid, payment_line_ids, {'date': vals.get('date_scheduled', False)}, context=context) - elif vals.get('date_prefered', False) == 'due': - vals.update({'date_scheduled': False}) - for order in self.browse(cr, uid, ids, context=context): - for line in order.line_ids: - payment_line_obj.write(cr, uid, [line.id], {'date': line.ml_maturity_date}, context=context) - elif vals.get('date_prefered', False) == 'now': - vals.update({'date_scheduled': False}) - for order in self.browse(cr, uid, ids, context=context): - for line in order.line_ids: - payment_line_ids.append(line.id) - payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context) - return super(payment_order, self).write(cr, uid, ids, vals, context=context) - -payment_order() - -class payment_line(osv.osv): - _name = 'payment.line' - _description = 'Payment Line' - - def translate(self, orig): - return { - "due_date": "date_maturity", - "reference": "ref"}.get(orig, orig) - - def info_owner(self, cr, uid, ids, name=None, args=None, context=None): - result = {} - for line in self.browse(cr, uid, ids, context=context): - owner = line.order_id.mode.bank_id.partner_id - result[line.id] = self._get_info_partner(cr, uid, owner, context=context) - return result - - def _get_info_partner(self,cr, uid, partner_record, context=None): - if not partner_record: - return False - st = partner_record.street or '' - st1 = partner_record.street2 or '' - zip = partner_record.zip or '' - city = partner_record.city or '' - zip_city = zip + ' ' + city - cntry = partner_record.country_id and partner_record.country_id.name or '' - return partner_record.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry - - def info_partner(self, cr, uid, ids, name=None, args=None, context=None): - result = {} - for line in self.browse(cr, uid, ids, context=context): - result[line.id] = False - if not line.partner_id: - break - result[line.id] = self._get_info_partner(cr, uid, line.partner_id, context=context) - return result - - #dead code - def select_by_name(self, cr, uid, ids, name, args, context=None): - if not ids: return {} - partner_obj = self.pool.get('res.partner') - - cr.execute("""SELECT pl.id, ml.%s - FROM account_move_line ml - INNER JOIN payment_line pl - ON (ml.id = pl.move_line_id) - WHERE pl.id IN %%s"""% self.translate(name), - (tuple(ids),)) - res = dict(cr.fetchall()) - - if name == 'partner_id': - partner_name = {} - for p_id, p_name in partner_obj.name_get(cr, uid, - filter(lambda x:x and x != 0,res.values()), context=context): - partner_name[p_id] = p_name - - for id in ids: - if id in res and partner_name: - res[id] = (res[id],partner_name[res[id]]) - else: - res[id] = (False,False) - else: - for id in ids: - res.setdefault(id, (False, "")) - return res - - def _amount(self, cursor, user, ids, name, args, context=None): - if not ids: - return {} - currency_obj = self.pool.get('res.currency') - if context is None: - context = {} - res = {} - - for line in self.browse(cursor, user, ids, context=context): - ctx = context.copy() - ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d') - res[line.id] = currency_obj.compute(cursor, user, line.currency.id, - line.company_currency.id, - line.amount_currency, context=ctx) - return res - - def _get_currency(self, cr, uid, context=None): - user_obj = self.pool.get('res.users') - currency_obj = self.pool.get('res.currency') - user = user_obj.browse(cr, uid, uid, context=context) - - if user.company_id: - return user.company_id.currency_id.id - else: - return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0] - - def _get_date(self, cr, uid, context=None): - if context is None: - context = {} - payment_order_obj = self.pool.get('payment.order') - date = False - - if context.get('order_id') and context['order_id']: - order = payment_order_obj.browse(cr, uid, context['order_id'], context=context) - if order.date_prefered == 'fixed': - date = order.date_scheduled - else: - date = time.strftime('%Y-%m-%d') - return date - - def _get_ml_inv_ref(self, cr, uid, ids, *a): - res = {} - for id in self.browse(cr, uid, ids): - res[id.id] = False - if id.move_line_id: - if id.move_line_id.invoice: - res[id.id] = id.move_line_id.invoice.id - return res - - def _get_ml_maturity_date(self, cr, uid, ids, *a): - res = {} - for id in self.browse(cr, uid, ids): - if id.move_line_id: - res[id.id] = id.move_line_id.date_maturity - else: - res[id.id] = False - return res - - def _get_ml_created_date(self, cr, uid, ids, *a): - res = {} - for id in self.browse(cr, uid, ids): - if id.move_line_id: - res[id.id] = id.move_line_id.date_created - else: - res[id.id] = False - return res - - _columns = { - 'name': fields.char('Your Reference', size=64, required=True), - 'communication': fields.char('Communication', size=64, required=True, help="Used as the message between ordering customer and current company. Depicts 'What do you want to say to the recipient about this order ?'"), - 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'), - 'move_line_id': fields.many2one('account.move.line', 'Entry line', domain=[('reconcile_id', '=', False), ('account_id.type', '=', 'payable')], help='This Entry Line will be referred for the information of the ordering customer.'), - 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2), - required=True, help='Payment amount in the partner currency'), - 'currency': fields.many2one('res.currency','Partner Currency', required=True), - 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True), - 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank Account'), - 'order_id': fields.many2one('payment.order', 'Order', required=True, - ondelete='cascade', select=True), - 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'), - 'amount': fields.function(_amount, string='Amount in Company Currency', - type='float', - help='Payment amount in the company currency'), - 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date", - type='date', help="Invoice Effective Date"), - 'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'), - 'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'), - 'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'), - 'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'), - 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"), - 'create_date': fields.datetime('Created', readonly=True), - 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True), - 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line'), - 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), - } - _defaults = { - 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence' - ).get(cursor, user, 'payment.line'), - 'state': 'normal', - 'currency': _get_currency, - 'company_currency': _get_currency, - 'date': _get_date, - } - _sql_constraints = [ - ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'), - ] - - def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None): - data = {} - move_line_obj = self.pool.get('account.move.line') - - data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False - - if move_line_id: - line = move_line_obj.browse(cr, uid, move_line_id, context=context) - data['amount_currency'] = line.amount_to_pay - - res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency, - company_currency, context) - if res: - data['amount'] = res['value']['amount'] - data['partner_id'] = line.partner_id.id - temp = line.currency_id and line.currency_id.id or False - if not temp: - if line.invoice: - data['currency'] = line.invoice.currency_id.id - else: - data['currency'] = temp - - # calling onchange of partner and updating data dictionary - temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type) - data.update(temp_dict['value']) - - data['communication'] = line.ref - - if date_prefered == 'now': - #no payment date => immediate payment - data['date'] = False - elif date_prefered == 'due': - data['date'] = line.date_maturity - elif date_prefered == 'fixed': - data['date'] = date_scheduled - return {'value': data} - - def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None): - if (not amount) or (not cmpny_currency): - return {'value': {'amount': False}} - res = {} - currency_obj = self.pool.get('res.currency') - company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount) - res['amount'] = company_amount - return {'value': res} - - def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None): - data = {} - partner_obj = self.pool.get('res.partner') - payment_mode_obj = self.pool.get('payment.mode') - data['info_partner'] = data['bank_id'] = False - - if partner_id: - part_obj = partner_obj.browse(cr, uid, partner_id, context=context) - partner = part_obj.name or '' - data['info_partner'] = self._get_info_partner(cr, uid, part_obj, context=context) - - if part_obj.bank_ids and payment_type: - bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context) - for bank in part_obj.bank_ids: - if bank.state in bank_type: - data['bank_id'] = bank.id - break - return {'value': data} - - def fields_get(self, cr, uid, fields=None, context=None): - res = super(payment_line, self).fields_get(cr, uid, fields, context) - if 'communication2' in res: - res['communication2'].setdefault('states', {}) - res['communication2']['states']['structured'] = [('readonly', True)] - res['communication2']['states']['normal'] = [('readonly', False)] - return res - -payment_line() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import logging +import time +from openerp.osv import fields, osv +from openerp import netsvc +_logger = logging.getLogger(__name__) + +class payment_mode(osv.osv): + _name = 'payment.mode' + _description = 'Payment Mode' + _columns = {'name': fields.char('Name', size=64, required=True, help='Mode of Payment'), + 'bank_id': fields.many2one('res.partner.bank', 'Bank account', required=True, help='Bank Account for the Payment Mode'), + 'journal': fields.many2one('account.journal', 'Journal', required=True, domain=[('type', 'in', ('bank', 'cash'))], help='Bank or Cash Journal for the Payment Mode'), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'partner_id': fields.related('company_id', 'partner_id', type='many2one', relation='res.partner', string='Partner', store=True)} + _defaults = {'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id} + + def suitable_bank_types(self, cr, uid, payment_code = None, context = None): + """Return the codes of the bank type that are suitable + for the given payment type code""" + if not payment_code: + return [] + cr.execute(' SELECT pb.state\n FROM res_partner_bank pb\n JOIN payment_mode pm ON (pm.bank_id = pb.id)\n WHERE pm.id = %s ', [payment_code]) + return [ x[0] for x in cr.fetchall() ] + + def onchange_company_id(self, cr, uid, ids, company_id = False, context = None): + result = {} + if company_id: + partner_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).partner_id.id + result['partner_id'] = partner_id + return {'value': result} + + +payment_mode() + +class payment_order(osv.osv): + _name = 'payment.order' + _description = 'Payment Order' + _rec_name = 'reference' + _order = 'id desc' + + def get_wizard(self, type): + _logger.warning("No wizard found for the payment type '%s'.", type) + return None + + def _total(self, cursor, user, ids, name, args, context = None): + if not ids: + return {} + res = {} + for order in self.browse(cursor, user, ids, context=context): + if order.line_ids: + res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0) + else: + res[order.id] = 0.0 + + return res + + _columns = {'date_scheduled': fields.date('Scheduled Date', states={'done': [('readonly', True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'), + 'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}), + 'mode': fields.many2one('payment.mode', 'Payment Mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'), + 'state': fields.selection([('draft', 'Draft'), + ('cancel', 'Cancelled'), + ('open', 'Confirmed'), + ('done', 'Done')], 'Status', select=True, help="When an order is placed the status is 'Draft'.\n Once the bank is confirmed the status is set to 'Confirmed'.\n Then the order is paid the status is 'Done'."), + 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}), + 'total': fields.function(_total, string='Total', type='float'), + 'user_id': fields.many2one('res.users', 'Responsible', required=True, states={'done': [('readonly', True)]}), + 'date_prefered': fields.selection([('now', 'Directly'), ('due', 'Due date'), ('fixed', 'Fixed date')], 'Preferred Date', change_default=True, required=True, states={'done': [('readonly', True)]}, help="Choose an option for the Payment Order:'Fixed' stands for a date specified by you.'Directly' stands for the direct execution.'Due date' stands for the scheduled date of execution."), + 'date_created': fields.date('Creation Date', readonly=True), + 'date_done': fields.date('Execution Date', readonly=True), + 'company_id': fields.related('mode', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True)} + _defaults = {'user_id': lambda self, cr, uid, context: uid, + 'state': 'draft', + 'date_prefered': 'due', + 'date_created': lambda *a: time.strftime('%Y-%m-%d'), + 'reference': lambda self, cr, uid, context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order')} + + def set_to_draft(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state': 'draft'}) + wf_service = netsvc.LocalService('workflow') + for id in ids: + wf_service.trg_create(uid, 'payment.order', id, cr) + + return True + + def action_open(self, cr, uid, ids, *args): + ir_seq_obj = self.pool.get('ir.sequence') + for order in self.read(cr, uid, ids, ['reference']): + if not order['reference']: + reference = ir_seq_obj.get(cr, uid, 'payment.order') + self.write(cr, uid, order['id'], {'reference': reference}) + + return True + + def set_done(self, cr, uid, ids, *args): + wf_service = netsvc.LocalService('workflow') + self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')}) + wf_service.trg_validate(uid, 'payment.order', ids[0], 'done', cr) + return True + + def copy(self, cr, uid, id, default = None, context = None): + if default is None: + default = {} + default.update({'state': 'draft', + 'line_ids': [], + 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')}) + return super(payment_order, self).copy(cr, uid, id, default, context=context) + + def write(self, cr, uid, ids, vals, context = None): + if context is None: + context = {} + payment_line_obj = self.pool.get('payment.line') + payment_line_ids = [] + if vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False) or vals.get('date_scheduled', False): + for order in self.browse(cr, uid, ids, context=context): + for line in order.line_ids: + payment_line_ids.append(line.id) + + payment_line_obj.write(cr, uid, payment_line_ids, {'date': vals.get('date_scheduled', False)}, context=context) + elif vals.get('date_prefered', False) == 'due': + vals.update({'date_scheduled': False}) + for order in self.browse(cr, uid, ids, context=context): + for line in order.line_ids: + payment_line_obj.write(cr, uid, [line.id], {'date': line.ml_maturity_date}, context=context) + + elif vals.get('date_prefered', False) == 'now': + vals.update({'date_scheduled': False}) + for order in self.browse(cr, uid, ids, context=context): + for line in order.line_ids: + payment_line_ids.append(line.id) + + payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context) + return super(payment_order, self).write(cr, uid, ids, vals, context=context) + + +payment_order() + +class payment_line(osv.osv): + _name = 'payment.line' + _description = 'Payment Line' + + def translate(self, orig): + return {'due_date': 'date_maturity', + 'reference': 'ref'}.get(orig, orig) + + def info_owner(self, cr, uid, ids, name = None, args = None, context = None): + result = {} + for line in self.browse(cr, uid, ids, context=context): + owner = line.order_id.mode.bank_id.partner_id + result[line.id] = self._get_info_partner(cr, uid, owner, context=context) + + return result + + def _get_info_partner(self, cr, uid, partner_record, context = None): + if not partner_record: + return False + st = partner_record.street or '' + st1 = partner_record.street2 or '' + zip = partner_record.zip or '' + city = partner_record.city or '' + zip_city = zip + ' ' + city + cntry = partner_record.country_id and partner_record.country_id.name or '' + return partner_record.name + '\n' + st + ' ' + st1 + '\n' + zip_city + '\n' + cntry + + def info_partner(self, cr, uid, ids, name = None, args = None, context = None): + result = {} + for line in self.browse(cr, uid, ids, context=context): + result[line.id] = False + if not line.partner_id: + break + result[line.id] = self._get_info_partner(cr, uid, line.partner_id, context=context) + + return result + + def select_by_name(self, cr, uid, ids, name, args, context = None): + if not ids: + return {} + partner_obj = self.pool.get('res.partner') + cr.execute('SELECT pl.id, ml.%s\n FROM account_move_line ml\n INNER JOIN payment_line pl\n ON (ml.id = pl.move_line_id)\n WHERE pl.id IN %%s' % self.translate(name), (tuple(ids),)) + res = dict(cr.fetchall()) + if name == 'partner_id': + partner_name = {} + for p_id, p_name in partner_obj.name_get(cr, uid, filter(lambda x: x and x != 0, res.values()), context=context): + partner_name[p_id] = p_name + + for id in ids: + if id in res and partner_name: + res[id] = (res[id], partner_name[res[id]]) + else: + res[id] = (False, False) + + else: + for id in ids: + res.setdefault(id, (False, '')) + + return res + + def _amount(self, cursor, user, ids, name, args, context = None): + if not ids: + return {} + else: + currency_obj = self.pool.get('res.currency') + if context is None: + context = {} + res = {} + for line in self.browse(cursor, user, ids, context=context): + ctx = context.copy() + ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d') + res[line.id] = currency_obj.compute(cursor, user, line.currency.id, line.company_currency.id, line.amount_currency, context=ctx) + + return res + + def _get_currency(self, cr, uid, context = None): + user_obj = self.pool.get('res.users') + currency_obj = self.pool.get('res.currency') + user = user_obj.browse(cr, uid, uid, context=context) + if user.company_id: + return user.company_id.currency_id.id + else: + return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0] + + def _get_date(self, cr, uid, context = None): + if context is None: + context = {} + payment_order_obj = self.pool.get('payment.order') + date = False + if context.get('order_id') and context['order_id']: + order = payment_order_obj.browse(cr, uid, context['order_id'], context=context) + if order.date_prefered == 'fixed': + date = order.date_scheduled + else: + date = time.strftime('%Y-%m-%d') + return date + + def _get_ml_inv_ref(self, cr, uid, ids, *a): + res = {} + for id in self.browse(cr, uid, ids): + res[id.id] = False + if id.move_line_id: + if id.move_line_id.invoice: + res[id.id] = id.move_line_id.invoice.id + + return res + + def _get_ml_maturity_date(self, cr, uid, ids, *a): + res = {} + for id in self.browse(cr, uid, ids): + if id.move_line_id: + res[id.id] = id.move_line_id.date_maturity + else: + res[id.id] = False + + return res + + def _get_ml_created_date(self, cr, uid, ids, *a): + res = {} + for id in self.browse(cr, uid, ids): + if id.move_line_id: + res[id.id] = id.move_line_id.date_created + else: + res[id.id] = False + + return res + + _columns = {'name': fields.char('Your Reference', size=64, required=True), + 'communication': fields.char('Communication', size=64, required=True, help="Used as the message between ordering customer and current company. Depicts 'What do you want to say to the recipient about this order ?'"), + 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'), + 'move_line_id': fields.many2one('account.move.line', 'Entry line', domain=[('reconcile_id', '=', False), ('account_id.type', '=', 'payable')], help='This Entry Line will be referred for the information of the ordering customer.'), + 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2), required=True, help='Payment amount in the partner currency'), + 'currency': fields.many2one('res.currency', 'Partner Currency', required=True), + 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True), + 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank Account'), + 'order_id': fields.many2one('payment.order', 'Order', required=True, ondelete='cascade', select=True), + 'partner_id': fields.many2one('res.partner', string='Partner', required=True, help='The Ordering Customer'), + 'amount': fields.function(_amount, string='Amount in Company Currency', type='float', help='Payment amount in the company currency'), + 'ml_date_created': fields.function(_get_ml_created_date, string='Effective Date', type='date', help='Invoice Effective Date'), + 'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'), + 'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'), + 'info_owner': fields.function(info_owner, string='Owner Account', type='text', help='Address of the Main Partner'), + 'info_partner': fields.function(info_partner, string='Destination Account', type='text', help='Address of the Ordering Customer.'), + 'date': fields.date('Payment Date', help='If no payment date is specified, the bank will treat this payment line directly'), + 'create_date': fields.datetime('Created', readonly=True), + 'state': fields.selection([('normal', 'Free'), ('structured', 'Structured')], 'Communication Type', required=True), + 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line'), + 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True)} + _defaults = {'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence').get(cursor, user, 'payment.line'), + 'state': 'normal', + 'currency': _get_currency, + 'company_currency': _get_currency, + 'date': _get_date} + _sql_constraints = [('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!')] + + def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency = False, company_currency = False, context = None): + data = {} + move_line_obj = self.pool.get('account.move.line') + data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False + if move_line_id: + line = move_line_obj.browse(cr, uid, move_line_id, context=context) + data['amount_currency'] = line.amount_to_pay + res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency, company_currency, context) + if res: + data['amount'] = res['value']['amount'] + data['partner_id'] = line.partner_id.id + temp = line.currency_id and line.currency_id.id or False + if not temp: + if line.invoice: + data['currency'] = line.invoice.currency_id.id + else: + data['currency'] = temp + temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type) + data.update(temp_dict['value']) + data['communication'] = line.ref + if date_prefered == 'now': + data['date'] = False + elif date_prefered == 'due': + data['date'] = line.date_maturity + elif date_prefered == 'fixed': + data['date'] = date_scheduled + return {'value': data} + + def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context = None): + if not amount or not cmpny_currency: + return {'value': {'amount': False}} + res = {} + currency_obj = self.pool.get('res.currency') + company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount) + res['amount'] = company_amount + return {'value': res} + + def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context = None): + data = {} + partner_obj = self.pool.get('res.partner') + payment_mode_obj = self.pool.get('payment.mode') + data['info_partner'] = data['bank_id'] = False + if partner_id: + part_obj = partner_obj.browse(cr, uid, partner_id, context=context) + partner = part_obj.name or '' + data['info_partner'] = self._get_info_partner(cr, uid, part_obj, context=context) + if part_obj.bank_ids and payment_type: + bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context) + for bank in part_obj.bank_ids: + if bank.state in bank_type: + data['bank_id'] = bank.id + break + + return {'value': data} + + def fields_get(self, cr, uid, fields = None, context = None): + res = super(payment_line, self).fields_get(cr, uid, fields, context) + if 'communication2' in res: + res['communication2'].setdefault('states', {}) + res['communication2']['states']['structured'] = [('readonly', True)] + res['communication2']['states']['normal'] = [('readonly', False)] + return res + + +payment_line() \ No newline at end of file diff --git a/addons/account_payment/account_payment_view.xml b/addons/account_payment/account_payment_view.xml index 89840b0225418..c2f27a576a2b0 100644 --- a/addons/account_payment/account_payment_view.xml +++ b/addons/account_payment/account_payment_view.xml @@ -64,7 +64,7 @@ - + diff --git a/addons/account_payment/images/payment_mode.jpeg b/addons/account_payment/images/payment_mode.jpeg new file mode 100644 index 0000000000000..21c694b49c6e6 Binary files /dev/null and b/addons/account_payment/images/payment_mode.jpeg differ diff --git a/addons/account_payment/images/payment_order.jpeg b/addons/account_payment/images/payment_order.jpeg new file mode 100644 index 0000000000000..46b40d7c544e8 Binary files /dev/null and b/addons/account_payment/images/payment_order.jpeg differ diff --git a/addons/account_payment/report/payment_order.py b/addons/account_payment/report/payment_order.py index 112476dd0809c..49329c368a83e 100644 --- a/addons/account_payment/report/payment_order.py +++ b/addons/account_payment/report/payment_order.py @@ -1,78 +1,75 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time - -from openerp import pooler -from openerp.report import report_sxw - -class payment_order(report_sxw.rml_parse): - - def __init__(self, cr, uid, name, context=None): - super(payment_order, self).__init__(cr, uid, name, context=context) - self.localcontext.update( { - 'time': time, - 'get_invoice_name': self._get_invoice_name, - 'get_amount_total_in_currency': self._get_amount_total_in_currency, - 'get_amount_total': self._get_amount_total, - 'get_account_name': self._get_account_name, - }) - - def _get_invoice_name(self, invoice_id): - if invoice_id: - pool = pooler.get_pool(self.cr.dbname) - value_name = pool.get('account.invoice').name_get(self.cr, self.uid, [invoice_id]) - if value_name: - return value_name[0][1] - return False - - def _get_amount_total_in_currency(self, payment): - total = 0.0 - if payment.line_ids: - currency_cmp = payment.line_ids[0].currency.id - else: - return False - for line in payment.line_ids: - if currency_cmp == line.currency.id: - total += line.amount_currency - else: - return False - return total - - def _get_amount_total(self, payment): - total = 0.0 - if not payment.line_ids: - return False - for line in payment.line_ids: - total += line.amount - return total - - def _get_account_name(self,bank_id): - if bank_id: - pool = pooler.get_pool(self.cr.dbname) - value_name = pool.get('res.partner.bank').name_get(self.cr, self.uid, [bank_id]) - if value_name: - return value_name[0][1] - return False - -report_sxw.report_sxw('report.payment.order', 'payment.order', 'addons/account_payment/report/payment_order.rml', parser=payment_order, header="external") - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from openerp import pooler +from openerp.report import report_sxw + +class payment_order(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context = None): + super(payment_order, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time, + 'get_invoice_name': self._get_invoice_name, + 'get_amount_total_in_currency': self._get_amount_total_in_currency, + 'get_amount_total': self._get_amount_total, + 'get_account_name': self._get_account_name}) + + def _get_invoice_name(self, invoice_id): + if invoice_id: + pool = pooler.get_pool(self.cr.dbname) + value_name = pool.get('account.invoice').name_get(self.cr, self.uid, [invoice_id]) + if value_name: + return value_name[0][1] + return False + + def _get_amount_total_in_currency(self, payment): + total = 0.0 + if payment.line_ids: + currency_cmp = payment.line_ids[0].currency.id + else: + return False + for line in payment.line_ids: + if currency_cmp == line.currency.id: + total += line.amount_currency + else: + return False + + return total + + def _get_amount_total(self, payment): + total = 0.0 + if not payment.line_ids: + return False + for line in payment.line_ids: + total += line.amount + + return total + + def _get_account_name(self, bank_id): + if bank_id: + pool = pooler.get_pool(self.cr.dbname) + value_name = pool.get('res.partner.bank').name_get(self.cr, self.uid, [bank_id]) + if value_name: + return value_name[0][1] + return False + + +report_sxw.report_sxw('report.payment.order', 'payment.order', 'addons/account_payment/report/payment_order.rml', parser=payment_order, header='external') \ No newline at end of file diff --git a/addons/account_payment/wizard/account_payment_order.py b/addons/account_payment/wizard/account_payment_order.py index 230cb7546298d..97d52b0f65363 100644 --- a/addons/account_payment/wizard/account_payment_order.py +++ b/addons/account_payment/wizard/account_payment_order.py @@ -1,125 +1,111 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time -from lxml import etree - -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -class payment_order_create(osv.osv_memory): - """ - Create a payment object with lines corresponding to the account move line - to pay according to the date and the mode provided by the user. - Hypothesis: - - Small number of non-reconciled move line, payment mode and bank account type, - - Big number of partner and bank account. - - If a type is given, unsuitable account Entry lines are ignored. - """ - - _name = 'payment.order.create' - _description = 'payment.order.create' - _columns = { - 'duedate': fields.date('Due Date', required=True), - 'entries': fields.many2many('account.move.line', 'line_pay_rel', 'pay_id', 'line_id', 'Entries') - } - _defaults = { - 'duedate': lambda *a: time.strftime('%Y-%m-%d'), - } - - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - if not context: context = {} - res = super(payment_order_create, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False) - if context and 'line_ids' in context: - doc = etree.XML(res['arch']) - nodes = doc.xpath("//field[@name='entries']") - for node in nodes: - node.set('domain', '[("id", "in", '+ str(context['line_ids'])+')]') - res['arch'] = etree.tostring(doc) - return res - - def create_payment(self, cr, uid, ids, context=None): - order_obj = self.pool.get('payment.order') - line_obj = self.pool.get('account.move.line') - payment_obj = self.pool.get('payment.line') - if context is None: - context = {} - data = self.browse(cr, uid, ids, context=context)[0] - line_ids = [entry.id for entry in data.entries] - if not line_ids: - return {'type': 'ir.actions.act_window_close'} - - payment = order_obj.browse(cr, uid, context['active_id'], context=context) - t = None - line2bank = line_obj.line2bank(cr, uid, line_ids, t, context) - - ## Finally populate the current payment with new lines: - for line in line_obj.browse(cr, uid, line_ids, context=context): - if payment.date_prefered == "now": - #no payment date => immediate payment - date_to_pay = False - elif payment.date_prefered == 'due': - date_to_pay = line.date_maturity - elif payment.date_prefered == 'fixed': - date_to_pay = payment.date_scheduled - payment_obj.create(cr, uid,{ - 'move_line_id': line.id, - 'amount_currency': line.amount_to_pay, - 'bank_id': line2bank.get(line.id), - 'order_id': payment.id, - 'partner_id': line.partner_id and line.partner_id.id or False, - 'communication': line.ref or '/', - 'state': line.invoice and line.invoice.reference_type != 'none' and 'structured' or 'normal', - 'date': date_to_pay, - 'currency': (line.invoice and line.invoice.currency_id.id) or line.journal_id.currency.id or line.journal_id.company_id.currency_id.id, - }, context=context) - return {'type': 'ir.actions.act_window_close'} - - def search_entries(self, cr, uid, ids, context=None): - line_obj = self.pool.get('account.move.line') - mod_obj = self.pool.get('ir.model.data') - if context is None: - context = {} - data = self.browse(cr, uid, ids, context=context)[0] - search_due_date = data.duedate -# payment = self.pool.get('payment.order').browse(cr, uid, context['active_id'], context=context) - - # Search for move line to pay: - domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)] - domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)] - line_ids = line_obj.search(cr, uid, domain, context=context) - context.update({'line_ids': line_ids}) - model_data_ids = mod_obj.search(cr, uid,[('model', '=', 'ir.ui.view'), ('name', '=', 'view_create_payment_order_lines')], context=context) - resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] - return {'name': _('Entry Lines'), - 'context': context, - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'payment.order.create', - 'views': [(resource_id,'form')], - 'type': 'ir.actions.act_window', - 'target': 'new', - } - -payment_order_create() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from lxml import etree +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class payment_order_create(osv.osv_memory): + """ + Create a payment object with lines corresponding to the account move line + to pay according to the date and the mode provided by the user. + Hypothesis: + - Small number of non-reconciled move line, payment mode and bank account type, + - Big number of partner and bank account. + + If a type is given, unsuitable account Entry lines are ignored. + """ + _name = 'payment.order.create' + _description = 'payment.order.create' + _columns = {'duedate': fields.date('Due Date', required=True), + 'entries': fields.many2many('account.move.line', 'line_pay_rel', 'pay_id', 'line_id', 'Entries')} + _defaults = {'duedate': lambda *a: time.strftime('%Y-%m-%d')} + + def fields_view_get(self, cr, uid, view_id = None, view_type = 'form', context = None, toolbar = False, submenu = False): + if not context: + context = {} + res = super(payment_order_create, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False) + if context and 'line_ids' in context: + doc = etree.XML(res['arch']) + nodes = doc.xpath("//field[@name='entries']") + for node in nodes: + node.set('domain', '[("id", "in", ' + str(context['line_ids']) + ')]') + + res['arch'] = etree.tostring(doc) + return res + + def create_payment(self, cr, uid, ids, context = None): + order_obj = self.pool.get('payment.order') + line_obj = self.pool.get('account.move.line') + payment_obj = self.pool.get('payment.line') + if context is None: + context = {} + data = self.browse(cr, uid, ids, context=context)[0] + line_ids = [ entry.id for entry in data.entries ] + if not line_ids: + return {'type': 'ir.actions.act_window_close'} + else: + payment = order_obj.browse(cr, uid, context['active_id'], context=context) + t = None + line2bank = line_obj.line2bank(cr, uid, line_ids, t, context) + for line in line_obj.browse(cr, uid, line_ids, context=context): + if payment.date_prefered == 'now': + date_to_pay = False + elif payment.date_prefered == 'due': + date_to_pay = line.date_maturity + elif payment.date_prefered == 'fixed': + date_to_pay = payment.date_scheduled + payment_obj.create(cr, uid, {'move_line_id': line.id, + 'amount_currency': line.amount_to_pay, + 'bank_id': line2bank.get(line.id), + 'order_id': payment.id, + 'partner_id': line.partner_id and line.partner_id.id or False, + 'communication': line.ref or '/', + 'state': line.invoice and line.invoice.reference_type != 'none' and 'structured' or 'normal', + 'date': date_to_pay, + 'currency': line.invoice and line.invoice.currency_id.id or line.journal_id.currency.id or line.journal_id.company_id.currency_id.id}, context=context) + + return {'type': 'ir.actions.act_window_close'} + + def search_entries(self, cr, uid, ids, context = None): + line_obj = self.pool.get('account.move.line') + mod_obj = self.pool.get('ir.model.data') + if context is None: + context = {} + data = self.browse(cr, uid, ids, context=context)[0] + search_due_date = data.duedate + domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)] + domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)] + line_ids = line_obj.search(cr, uid, domain, context=context) + context.update({'line_ids': line_ids}) + model_data_ids = mod_obj.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_create_payment_order_lines')], context=context) + resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] + return {'name': _('Entry Lines'), + 'context': context, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'payment.order.create', + 'views': [(resource_id, 'form')], + 'type': 'ir.actions.act_window', + 'target': 'new'} + + +payment_order_create() \ No newline at end of file diff --git a/addons/account_payment/wizard/account_payment_pay.py b/addons/account_payment/wizard/account_payment_pay.py index 03ff46125f589..549cbb9a5a120 100644 --- a/addons/account_payment/wizard/account_payment_pay.py +++ b/addons/account_payment/wizard/account_payment_pay.py @@ -1,59 +1,39 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import osv - -#TODO:REMOVE this wizard is not used -class account_payment_make_payment(osv.osv_memory): - _name = "account.payment.make.payment" - _description = "Account make payment" - - def launch_wizard(self, cr, uid, ids, context=None): - """ - Search for a wizard to launch according to the type. - If type is manual. just confirm the order. - """ - obj_payment_order = self.pool.get('payment.order') - if context is None: - context = {} -# obj_model = self.pool.get('ir.model.data') -# obj_act = self.pool.get('ir.actions.act_window') -# order = obj_payment_order.browse(cr, uid, context['active_id'], context) - obj_payment_order.set_done(cr, uid, [context['active_id']], context) - return {'type': 'ir.actions.act_window_close'} -# t = order.mode and order.mode.type.code or 'manual' -# if t == 'manual': -# obj_payment_order.set_done(cr,uid,context['active_id'],context) -# return {} -# -# gw = obj_payment_order.get_wizard(t) -# if not gw: -# obj_payment_order.set_done(cr,uid,context['active_id'],context) -# return {} -# -# module, wizard= gw -# result = obj_model._get_id(cr, uid, module, wizard) -# id = obj_model.read(cr, uid, [result], ['res_id'])[0]['res_id'] -# return obj_act.read(cr, uid, [id])[0] - -account_payment_make_payment() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import osv + +class account_payment_make_payment(osv.osv_memory): + _name = 'account.payment.make.payment' + _description = 'Account make payment' + + def launch_wizard(self, cr, uid, ids, context = None): + """ + Search for a wizard to launch according to the type. + If type is manual. just confirm the order. + """ + obj_payment_order = self.pool.get('payment.order') + if context is None: + context = {} + obj_payment_order.set_done(cr, uid, [context['active_id']], context) + return {'type': 'ir.actions.act_window_close'} + + +account_payment_make_payment() \ No newline at end of file diff --git a/addons/account_payment/wizard/account_payment_populate_statement.py b/addons/account_payment/wizard/account_payment_populate_statement.py index 03a6704785821..9db8f872c020b 100644 --- a/addons/account_payment/wizard/account_payment_populate_statement.py +++ b/addons/account_payment/wizard/account_payment_populate_statement.py @@ -1,124 +1,102 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time -from lxml import etree - -from openerp.osv import fields, osv - -class account_payment_populate_statement(osv.osv_memory): - _name = "account.payment.populate.statement" - _description = "Account Payment Populate Statement" - _columns = { - 'lines': fields.many2many('payment.line', 'payment_line_rel_', 'payment_id', 'line_id', 'Payment Lines') - } - - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - line_obj = self.pool.get('payment.line') - - res = super(account_payment_populate_statement, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False) - line_ids = line_obj.search(cr, uid, [ - ('move_line_id.reconcile_id', '=', False), - ('bank_statement_line_id', '=', False), - ('move_line_id.state','=','valid')]) - line_ids.extend(line_obj.search(cr, uid, [ - ('move_line_id.reconcile_id', '=', False), - ('order_id.mode', '=', False), - ('move_line_id.state','=','valid')])) - domain = '[("id", "in", '+ str(line_ids)+')]' - doc = etree.XML(res['arch']) - nodes = doc.xpath("//field[@name='lines']") - for node in nodes: - node.set('domain', domain) - res['arch'] = etree.tostring(doc) - return res - - def populate_statement(self, cr, uid, ids, context=None): - line_obj = self.pool.get('payment.line') - statement_obj = self.pool.get('account.bank.statement') - statement_line_obj = self.pool.get('account.bank.statement.line') - currency_obj = self.pool.get('res.currency') - voucher_obj = self.pool.get('account.voucher') - voucher_line_obj = self.pool.get('account.voucher.line') - move_line_obj = self.pool.get('account.move.line') - - if context is None: - context = {} - data = self.read(cr, uid, ids, [], context=context)[0] - line_ids = data['lines'] - if not line_ids: - return {'type': 'ir.actions.act_window_close'} - - statement = statement_obj.browse(cr, uid, context['active_id'], context=context) - - for line in line_obj.browse(cr, uid, line_ids, context=context): - ctx = context.copy() - ctx['date'] = line.ml_maturity_date # was value_date earlier,but this field exists no more now - amount = currency_obj.compute(cr, uid, line.currency.id, - statement.currency.id, line.amount_currency, context=ctx) - - if not line.move_line_id.id: - continue - context.update({'move_line_ids': [line.move_line_id.id]}) - result = voucher_obj.onchange_partner_id(cr, uid, [], partner_id=line.partner_id.id, journal_id=statement.journal_id.id, amount=abs(amount), currency_id= statement.currency.id, ttype='payment', date=line.ml_maturity_date, context=context) - - if line.move_line_id: - voucher_res = { - 'type': 'payment', - 'name': line.name, - 'partner_id': line.partner_id.id, - 'journal_id': statement.journal_id.id, - 'account_id': result['value'].get('account_id', statement.journal_id.default_credit_account_id.id), - 'company_id': statement.company_id.id, - 'currency_id': statement.currency.id, - 'date': line.date or time.strftime('%Y-%m-%d'), - 'amount': abs(amount), - 'period_id': statement.period_id.id, - 'payment_rate_currency_id': statement.currency.id, - } - voucher_id = voucher_obj.create(cr, uid, voucher_res, context=context) - - voucher_line_dict = {} - for line_dict in result['value']['line_cr_ids'] + result['value']['line_dr_ids']: - move_line = move_line_obj.browse(cr, uid, line_dict['move_line_id'], context) - if line.move_line_id.move_id.id == move_line.move_id.id: - voucher_line_dict = line_dict - - if voucher_line_dict: - voucher_line_dict.update({'voucher_id': voucher_id}) - voucher_line_obj.create(cr, uid, voucher_line_dict, context=context) - st_line_id = statement_line_obj.create(cr, uid, { - 'name': line.order_id.reference or '?', - 'amount': - amount, - 'type': 'supplier', - 'partner_id': line.partner_id.id, - 'account_id': line.move_line_id.account_id.id, - 'statement_id': statement.id, - 'ref': line.communication, - 'voucher_id': voucher_id, - }, context=context) - - line_obj.write(cr, uid, [line.id], {'bank_statement_line_id': st_line_id}) - return {'type': 'ir.actions.act_window_close'} - -account_payment_populate_statement() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from lxml import etree +from openerp.osv import fields, osv + +class account_payment_populate_statement(osv.osv_memory): + _name = 'account.payment.populate.statement' + _description = 'Account Payment Populate Statement' + _columns = {'lines': fields.many2many('payment.line', 'payment_line_rel_', 'payment_id', 'line_id', 'Payment Lines')} + + def fields_view_get(self, cr, uid, view_id = None, view_type = 'form', context = None, toolbar = False, submenu = False): + line_obj = self.pool.get('payment.line') + res = super(account_payment_populate_statement, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False) + line_ids = line_obj.search(cr, uid, [('move_line_id.reconcile_id', '=', False), ('bank_statement_line_id', '=', False), ('move_line_id.state', '=', 'valid')]) + line_ids.extend(line_obj.search(cr, uid, [('move_line_id.reconcile_id', '=', False), ('order_id.mode', '=', False), ('move_line_id.state', '=', 'valid')])) + domain = '[("id", "in", ' + str(line_ids) + ')]' + doc = etree.XML(res['arch']) + nodes = doc.xpath("//field[@name='lines']") + for node in nodes: + node.set('domain', domain) + + res['arch'] = etree.tostring(doc) + return res + + def populate_statement(self, cr, uid, ids, context = None): + line_obj = self.pool.get('payment.line') + statement_obj = self.pool.get('account.bank.statement') + statement_line_obj = self.pool.get('account.bank.statement.line') + currency_obj = self.pool.get('res.currency') + voucher_obj = self.pool.get('account.voucher') + voucher_line_obj = self.pool.get('account.voucher.line') + move_line_obj = self.pool.get('account.move.line') + if context is None: + context = {} + data = self.read(cr, uid, ids, [], context=context)[0] + line_ids = data['lines'] + if not line_ids: + return {'type': 'ir.actions.act_window_close'} + else: + statement = statement_obj.browse(cr, uid, context['active_id'], context=context) + for line in line_obj.browse(cr, uid, line_ids, context=context): + ctx = context.copy() + ctx['date'] = line.ml_maturity_date + amount = currency_obj.compute(cr, uid, line.currency.id, statement.currency.id, line.amount_currency, context=ctx) + if not line.move_line_id.id: + continue + context.update({'move_line_ids': [line.move_line_id.id]}) + result = voucher_obj.onchange_partner_id(cr, uid, [], partner_id=line.partner_id.id, journal_id=statement.journal_id.id, amount=abs(amount), currency_id=statement.currency.id, ttype='payment', date=line.ml_maturity_date, context=context) + if line.move_line_id: + voucher_res = {'type': 'payment', + 'name': line.name, + 'partner_id': line.partner_id.id, + 'journal_id': statement.journal_id.id, + 'account_id': result['value'].get('account_id', statement.journal_id.default_credit_account_id.id), + 'company_id': statement.company_id.id, + 'currency_id': statement.currency.id, + 'date': line.date or time.strftime('%Y-%m-%d'), + 'amount': abs(amount), + 'period_id': statement.period_id.id} + voucher_id = voucher_obj.create(cr, uid, voucher_res, context=context) + voucher_line_dict = {} + for line_dict in result['value']['line_cr_ids'] + result['value']['line_dr_ids']: + move_line = move_line_obj.browse(cr, uid, line_dict['move_line_id'], context) + if line.move_line_id.move_id.id == move_line.move_id.id: + voucher_line_dict = line_dict + + if voucher_line_dict: + voucher_line_dict.update({'voucher_id': voucher_id}) + voucher_line_obj.create(cr, uid, voucher_line_dict, context=context) + st_line_id = statement_line_obj.create(cr, uid, {'name': line.order_id.reference or '?', + 'amount': -amount, + 'type': 'supplier', + 'partner_id': line.partner_id.id, + 'account_id': line.move_line_id.account_id.id, + 'statement_id': statement.id, + 'ref': line.communication, + 'voucher_id': voucher_id}, context=context) + line_obj.write(cr, uid, [line.id], {'bank_statement_line_id': st_line_id}) + + return {'type': 'ir.actions.act_window_close'} + + +account_payment_populate_statement() \ No newline at end of file diff --git a/addons/account_report_company/account_report_company.py b/addons/account_report_company/account_report_company.py index be77be8d0b62c..c56e8ba19a750 100644 --- a/addons/account_report_company/account_report_company.py +++ b/addons/account_report_company/account_report_company.py @@ -1,58 +1,39 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (c) 2013 S.A. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import osv, fields - -class res_partner(osv.Model): - _inherit = 'res.partner' - _order = 'display_name' - - def _display_name_compute(self, cr, uid, ids, name, args, context=None): - context = dict(context or {}) - context.pop('show_address', None) - return dict(self.name_get(cr, uid, ids, context=context)) - - _display_name_store_triggers = { - 'res.partner': (lambda self,cr,uid,ids,context=None: self.search(cr, uid, [('id','child_of',ids)], context=dict(active_test=False)), - ['parent_id', 'is_company', 'name'], 10) - } - - # indirection to avoid passing a copy of the overridable method when declaring the function field - _display_name = lambda self, *args, **kwargs: self._display_name_compute(*args, **kwargs) - - _columns = { - # extra field to allow ORDER BY to match visible names - 'display_name': fields.function(_display_name, type='char', string='Name', store=_display_name_store_triggers, select=1), - } - - def _get_display_name(self, unaccent): - # use stored display name for better performances - return unaccent('res_partner.display_name') - - -class account_invoice(osv.Model): - _inherit = 'account.invoice' - - _columns = { - 'commercial_partner_id': fields.related('partner_id', 'commercial_partner_id', string='Commercial Entity', type='many2one', - relation='res.partner', store=True, readonly=True, - help="The commercial entity that will be used on Journal Entries for this invoice") - } +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import osv, fields + +class res_partner(osv.Model): + _inherit = 'res.partner' + _order = 'display_name' + + def _display_name_compute(self, cr, uid, ids, name, args, context = None): + context = dict(context or {}) + context.pop('show_address', None) + return dict(self.name_get(cr, uid, ids, context=context)) + + _display_name_store_triggers = {'res.partner': (lambda self, cr, uid, ids, context = None: self.search(cr, uid, [('id', 'child_of', ids)]), ['parent_id', 'is_company', 'name'], 10)} + _display_name = lambda self, *args, **kwargs: self._display_name_compute(*args, **kwargs) + _columns = {'display_name': fields.function(_display_name, type='char', string='Name', store=_display_name_store_triggers)} + + +class account_invoice(osv.Model): + _inherit = 'account.invoice' + _columns = {'commercial_partner_id': fields.related('partner_id', 'commercial_partner_id', string='Commercial Entity', type='many2one', relation='res.partner', store=True, readonly=True, help='The commercial entity that will be used on Journal Entries for this invoice')} \ No newline at end of file diff --git a/addons/account_report_company/report/account_invoice_report.py b/addons/account_report_company/report/account_invoice_report.py index 5941aef9e6e75..fc46cb07cf09c 100644 --- a/addons/account_report_company/report/account_invoice_report.py +++ b/addons/account_report_company/report/account_invoice_report.py @@ -1,36 +1,34 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (c) 2013 OpenERP S.A. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp.osv import osv, fields - -class account_invoice_report(osv.Model): - _inherit = 'account.invoice.report' - _columns = { - 'commercial_partner_id': fields.many2one('res.partner', 'Partner Company', help="Commercial Entity"), - } - - def _select(self): - return super(account_invoice_report, self)._select() + ", sub.commercial_partner_id as commercial_partner_id" - - def _sub_select(self): - return super(account_invoice_report, self)._sub_select() + ", ai.commercial_partner_id as commercial_partner_id" - - def _group_by(self): - return super(account_invoice_report, self)._group_by() + ", ai.commercial_partner_id" +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import osv, fields + +class account_invoice_report(osv.Model): + _inherit = 'account.invoice.report' + _columns = {'commercial_partner_id': fields.many2one('res.partner', 'Partner Company', help='Commercial Entity')} + + def _select(self): + return super(account_invoice_report, self)._select() + ', sub.commercial_partner_id as commercial_partner_id' + + def _sub_select(self): + return super(account_invoice_report, self)._sub_select() + ', ai.commercial_partner_id as commercial_partner_id' + + def _group_by(self): + return super(account_invoice_report, self)._group_by() + ', ai.commercial_partner_id' \ No newline at end of file diff --git a/addons/account_sequence/images/internal_sequence_number.jpeg b/addons/account_sequence/images/internal_sequence_number.jpeg new file mode 100644 index 0000000000000..26820639ae33d Binary files /dev/null and b/addons/account_sequence/images/internal_sequence_number.jpeg differ diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index be279aa6fe560..9978f2a70cd95 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -70,13 +70,13 @@ class account_config_settings(osv.osv_memory): type='many2one', relation='account.account', string="Gain Exchange Rate Account", - domain="[('type', '=', 'other'), ('company_id', '=', company_id)]"), + domain="[('type', '=', 'other')]"), 'expense_currency_exchange_account_id': fields.related( 'company_id', 'expense_currency_exchange_account_id', type="many2one", relation='account.account', string="Loss Exchange Rate Account", - domain="[('type', '=', 'other'), ('company_id', '=', company_id)]"), + domain="[('type', '=', 'other')]"), } def onchange_company_id(self, cr, uid, ids, company_id, context=None): res = super(account_config_settings, self).onchange_company_id(cr, uid, ids, company_id, context=context) @@ -89,23 +89,6 @@ def onchange_company_id(self, cr, uid, ids, company_id, context=None): 'expense_currency_exchange_account_id': False}) return res - def _check_account_gain(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids, context=context): - if obj.income_currency_exchange_account_id.company_id and obj.company_id != obj.income_currency_exchange_account_id.company_id: - return False - return True - - def _check_account_loss(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids, context=context): - if obj.expense_currency_exchange_account_id.company_id and obj.company_id != obj.expense_currency_exchange_account_id.company_id: - return False - return True - - _constraints = [ - (_check_account_gain, 'The company of the gain exchange rate account must be the same than the company selected.', ['income_currency_exchange_account_id']), - (_check_account_loss, 'The company of the loss exchange rate account must be the same than the company selected.', ['expense_currency_exchange_account_id']), - ] - class account_voucher(osv.osv): def _check_paid(self, cr, uid, ids, name, args, context=None): res = {} @@ -243,7 +226,7 @@ def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, if context.get('type', 'sale') in ('purchase', 'payment'): nodes = doc.xpath("//field[@name='partner_id']") for node in nodes: - node.set('context', "{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}") + node.set('context', "{'search_default_supplier': 1}") if context.get('invoice_type','') in ('in_invoice', 'in_refund'): node.set('string', _("Supplier")) res['arch'] = etree.tostring(doc) @@ -351,7 +334,13 @@ def _fnct_currency_help_label(self, cr, uid, ids, name, args, context=None): 'account_voucher.mt_voucher_state_change': lambda self, cr, uid, obj, ctx=None: True, }, } - + def _get_date(self, cr, uid, context=None): + if context is None: + context = {} + query="""select now()::date as todays_date""" + cr.execute(query) + for item in cr.dictfetchall(): + return item['todays_date'] _columns = { 'active': fields.boolean('Active', help="By default, reconciliation vouchers made on draft bank statements are set as inactive, which allow to hide the customer/supplier payment while the bank statement isn't confirmed."), 'type':fields.selection([ @@ -384,7 +373,7 @@ def _fnct_currency_help_label(self, cr, uid, ids, name, args, context=None): \n* The \'Posted\' status is used when user create voucher,a voucher number is generated and voucher entries are created in account \ \n* The \'Cancelled\' status is used when user cancel voucher.'), 'amount': fields.float('Total', digits_compute=dp.get_precision('Account'), required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True), + 'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}), 'reference': fields.char('Ref #', size=64, readonly=True, states={'draft':[('readonly',False)]}, help="Transaction reference number."), 'number': fields.char('Number', size=32, readonly=True,), 'move_id':fields.many2one('account.move', 'Account Entry'), @@ -427,7 +416,7 @@ def _fnct_currency_help_label(self, cr, uid, ids, name, args, context=None): 'state': 'draft', 'pay_now': 'pay_now', 'name': '', - 'date': fields.date.context_today, + 'date': _get_date, 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.voucher',context=c), 'tax_id': _get_tax, 'payment_option': 'without_writeoff', @@ -573,12 +562,7 @@ def onchange_journal_voucher(self, cr, uid, ids, line_ids=False, tax_id=False, p else: if not journal.default_credit_account_id or not journal.default_debit_account_id: raise osv.except_osv(_('Error!'), _('Please define default credit/debit accounts on the journal "%s".') % (journal.name)) - if ttype in ('sale', 'receipt'): - account_id = journal.default_debit_account_id.id - elif ttype in ('purchase', 'payment'): - account_id = journal.default_credit_account_id.id - else: - account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id + account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id tr_type = 'receipt' default['value']['account_id'] = account_id @@ -670,10 +654,6 @@ def basic_onchange_partner(self, cr, uid, ids, partner_id, journal_id, ttype, co account_id = partner.property_account_receivable.id elif journal.type in ('purchase', 'purchase_refund','expense'): account_id = partner.property_account_payable.id - elif ttype in ('sale', 'receipt'): - account_id = journal.default_debit_account_id.id - elif ttype in ('purchase', 'payment'): - account_id = journal.default_credit_account_id.id else: account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id @@ -767,17 +747,13 @@ def _remove_noise_in_o2m(): total_credit = 0.0 total_debit = 0.0 - account_type = None - if context.get('account_id'): - account_type = self.pool['account.account'].browse(cr, uid, context['account_id'], context=context).type + account_type = 'receivable' if ttype == 'payment': - if not account_type: - account_type = 'payable' + account_type = 'payable' total_debit = price or 0.0 else: total_credit = price or 0.0 - if not account_type: - account_type = 'receivable' + account_type = 'receivable' if not context.get('move_line_ids', False): ids = move_line_pool.search(cr, uid, [('state','=','valid'), ('account_id.type', '=', account_type), ('reconcile_id', '=', False), ('partner_id', '=', partner_id)], context=context) @@ -785,7 +761,7 @@ def _remove_noise_in_o2m(): ids = context['move_line_ids'] invoice_id = context.get('invoice_id', False) company_currency = journal.company_id.currency_id.id - move_lines_found = [] + move_line_found = False #order the lines by most old first ids.reverse() @@ -800,25 +776,25 @@ def _remove_noise_in_o2m(): if line.invoice.id == invoice_id: #if the invoice linked to the voucher line is equal to the invoice_id in context #then we assign the amount on that line, whatever the other voucher lines - move_lines_found.append(line.id) + move_line_found = line.id + break elif currency_id == company_currency: #otherwise treatments is the same but with other field names if line.amount_residual == price: #if the amount residual is equal the amount voucher, we assign it to that voucher #line, whatever the other voucher lines - move_lines_found.append(line.id) + move_line_found = line.id break #otherwise we will split the voucher amount on each line (by most old first) total_credit += line.credit or 0.0 total_debit += line.debit or 0.0 elif currency_id == line.currency_id.id: if line.amount_residual_currency == price: - move_lines_found.append(line.id) + move_line_found = line.id break total_credit += line.credit and line.amount_currency or 0.0 total_debit += line.debit and line.amount_currency or 0.0 - remaining_amount = price #voucher line creation for line in account_move_lines: @@ -839,16 +815,15 @@ def _remove_noise_in_o2m(): 'move_line_id':line.id, 'account_id':line.account_id.id, 'amount_original': amount_original, - 'amount': (line.id in move_lines_found) and min(abs(remaining_amount), amount_unreconciled) or 0.0, + 'amount': (move_line_found == line.id) and min(abs(price), amount_unreconciled) or 0.0, 'date_original':line.date, 'date_due':line.date_maturity, 'amount_unreconciled': amount_unreconciled, 'currency_id': line_currency_id, } - remaining_amount -= rs['amount'] #in case a corresponding move_line hasn't been found, we now try to assign the voucher amount #on existing invoices: we split voucher amount by most old first, but only for lines in the same currency - if not move_lines_found: + if not move_line_found: if currency_id == line_currency_id: if line.credit: amount = min(amount_unreconciled, abs(total_debit)) @@ -867,9 +842,9 @@ def _remove_noise_in_o2m(): else: default['value']['line_dr_ids'].append(rs) - if len(default['value']['line_cr_ids']) > 0: + if ttype == 'payment' and len(default['value']['line_cr_ids']) > 0: default['value']['pre_line'] = 1 - elif len(default['value']['line_dr_ids']) > 0: + elif ttype == 'receipt' and len(default['value']['line_dr_ids']) > 0: default['value']['pre_line'] = 1 default['value']['writeoff_amount'] = self._compute_writeoff_amount(cr, uid, default['value']['line_dr_ids'], default['value']['line_cr_ids'], price, ttype) return default @@ -930,12 +905,7 @@ def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_i return False journal_pool = self.pool.get('account.journal') journal = journal_pool.browse(cr, uid, journal_id, context=context) - if ttype in ('sale', 'receipt'): - account_id = journal.default_debit_account_id - elif ttype in ('purchase', 'payment'): - account_id = journal.default_credit_account_id - else: - account_id = journal.default_credit_account_id or journal.default_debit_account_id + account_id = journal.default_credit_account_id or journal.default_debit_account_id tax_id = False if account_id and account_id.tax_ids: tax_id = account_id.tax_ids[0].id @@ -949,17 +919,16 @@ def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_i currency_id = journal.currency.id else: currency_id = journal.company_id.currency_id.id - vals['value'].update({'currency_id': currency_id, 'payment_rate_currency_id': currency_id}) + vals['value'].update({'currency_id': currency_id}) #in case we want to register the payment directly from an invoice, it's confusing to allow to switch the journal #without seeing that the amount is expressed in the journal currency, and not in the invoice currency. So to avoid #this common mistake, we simply reset the amount to 0 if the currency is not the invoice currency. if context.get('payment_expected_currency') and currency_id != context.get('payment_expected_currency'): vals['value']['amount'] = 0 amount = 0 - if partner_id: - res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context) - for key in res.keys(): - vals[key].update(res[key]) + res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context) + for key in res.keys(): + vals[key].update(res[key]) return vals def button_proforma_voucher(self, cr, uid, ids, context=None): @@ -983,19 +952,34 @@ def action_cancel_draft(self, cr, uid, ids, context=None): def cancel_voucher(self, cr, uid, ids, context=None): reconcile_pool = self.pool.get('account.move.reconcile') move_pool = self.pool.get('account.move') - move_line_pool = self.pool.get('account.move.line') + for voucher in self.browse(cr, uid, ids, context=context): # refresh to make sure you don't unlink an already removed move + #oanhle cap nhat lai hop dong + from datetime import datetime + name=voucher.number + if name: + query="""select bhht.id from icsc_hopdong_banhang_hinhthuc bhht + left join icsc_hinhthuc ht on ht.id=bhht.name + where ht.name= '"""+str(name)+"""'""" + cr.execute(query) + for item in cr.dictfetchall(): + bh_ht_id=item['id'] + today =time.strftime('%Y-%m-%d') + ngay_kethuc=datetime.strptime(today,'%Y-%m-%d') + self.pool.get('icsc.hopdong.banhang.hinhthuc').write(cr, uid, [bh_ht_id],{'ngay_kethuc':ngay_kethuc}, context) + voucher.refresh() + recs = [] for line in voucher.move_ids: - # refresh to make sure you don't unreconcile an already unreconciled entry - line.refresh() if line.reconcile_id: - move_lines = [move_line.id for move_line in line.reconcile_id.line_id] - move_lines.remove(line.id) + recs += [line.reconcile_id.id] reconcile_pool.unlink(cr, uid, [line.reconcile_id.id]) - if len(move_lines) >= 2: - move_line_pool.reconcile_partial(cr, uid, move_lines, 'auto',context=context) + if line.reconcile_partial_id: + recs += [line.reconcile_partial_id.id] + + + if voucher.move_id: move_pool.button_cancel(cr, uid, [voucher.move_id.id]) move_pool.unlink(cr, uid, [voucher.move_id.id]) @@ -1016,7 +1000,7 @@ def onchange_payment(self, cr, uid, ids, pay_now, journal_id, partner_id, ttype= res = {} if not partner_id: return res - res = {} + res = {'account_id':False} partner_pool = self.pool.get('res.partner') journal_pool = self.pool.get('account.journal') if pay_now == 'pay_later': @@ -1026,14 +1010,9 @@ def onchange_payment(self, cr, uid, ids, pay_now, journal_id, partner_id, ttype= account_id = partner.property_account_receivable.id elif journal.type in ('purchase', 'purchase_refund','expense'): account_id = partner.property_account_payable.id - elif ttype in ('sale', 'receipt'): - account_id = journal.default_debit_account_id.id - elif ttype in ('purchase', 'payment'): - account_id = journal.default_credit_account_id.id else: account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id - if account_id: - res['account_id'] = account_id + res['account_id'] = account_id return {'value':res} def _sel_context(self, cr, uid, voucher_id, context=None): @@ -1067,6 +1046,8 @@ def first_move_line_get(self, cr, uid, voucher_id, move_id, company_currency, cu :rtype: dict ''' voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context) + + debit = credit = 0.0 # TODO: is there any other alternative then the voucher type ?? # ANSWER: We can have payment and receipt "In Advance". @@ -1090,11 +1071,11 @@ def first_move_line_get(self, cr, uid, voucher_id, move_id, company_currency, cu 'period_id': voucher.period_id.id, 'partner_id': voucher.partner_id.id, 'currency_id': company_currency <> current_currency and current_currency or False, - 'amount_currency': (sign * abs(voucher.amount) # amount < 0 for refunds - if company_currency != current_currency else 0.0), + 'amount_currency': company_currency <> current_currency and sign * voucher.amount or 0.0, 'date': voucher.date, 'date_maturity': voucher.date_due } + return move_line def account_move_get(self, cr, uid, voucher_id, context=None): @@ -1253,7 +1234,7 @@ def voucher_move_line_create(self, cr, uid, voucher_id, line_total, move_id, com if line.amount == line.amount_unreconciled: if not line.move_line_id: raise osv.except_osv(_('Wrong voucher line'),_("The invoice you are willing to pay is not valid anymore.")) - sign = line.type =='dr' and -1 or 1 + sign = voucher.type in ('payment', 'purchase') and -1 or 1 currency_rate_difference = sign * (line.move_line_id.amount_residual - amount) else: currency_rate_difference = 0.0 @@ -1311,7 +1292,8 @@ def voucher_move_line_create(self, cr, uid, voucher_id, line_total, move_id, com # otherwise we use the rates of the system amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx) if line.amount == line.amount_unreconciled: - foreign_currency_diff = line.move_line_id.amount_residual_currency - abs(amount_currency) + sign = voucher.type in ('payment', 'purchase') and -1 or 1 + foreign_currency_diff = sign * line.move_line_id.amount_residual_currency + amount_currency move_line['amount_currency'] = amount_currency voucher_line = move_line_obj.create(cr, uid, move_line) @@ -1372,14 +1354,10 @@ def writeoff_move_line_get(self, cr, uid, voucher_id, line_total, move_id, name, if voucher.payment_option == 'with_writeoff': account_id = voucher.writeoff_acc_id.id write_off_name = voucher.comment - elif voucher.partner_id: - if voucher.type in ('sale', 'receipt'): - account_id = voucher.partner_id.property_account_receivable.id - else: - account_id = voucher.partner_id.property_account_payable.id + elif voucher.type in ('sale', 'receipt'): + account_id = voucher.partner_id.property_account_receivable.id else: - # fallback on account of voucher - account_id = voucher.account_id.id + account_id = voucher.partner_id.property_account_payable.id sign = voucher.type == 'payment' and -1 or 1 move_line = { 'name': write_off_name or name, @@ -1389,7 +1367,7 @@ def writeoff_move_line_get(self, cr, uid, voucher_id, line_total, move_id, name, 'date': voucher.date, 'credit': diff > 0 and diff or 0.0, 'debit': diff < 0 and -diff or 0.0, - 'amount_currency': company_currency <> current_currency and (sign * -1 * voucher.writeoff_amount) or 0.0, + 'amount_currency': company_currency <> current_currency and (sign * -1 * voucher.writeoff_amount) or False, 'currency_id': company_currency <> current_currency and current_currency or False, 'analytic_account_id': voucher.analytic_id and voucher.analytic_id.id or False, } @@ -1426,7 +1404,7 @@ def action_move_line_create(self, cr, uid, ids, context=None): move_pool = self.pool.get('account.move') move_line_pool = self.pool.get('account.move.line') for voucher in self.browse(cr, uid, ids, context=context): - local_context = dict(context, force_company=voucher.journal_id.company_id.id) + if voucher.move_id: continue company_currency = self._get_company_currency(cr, uid, voucher.id, context) @@ -1440,22 +1418,41 @@ def action_move_line_create(self, cr, uid, ids, context=None): move_id = move_pool.create(cr, uid, self.account_move_get(cr, uid, voucher.id, context=context), context=context) # Get the name of the account_move just created name = move_pool.browse(cr, uid, move_id, context=context).name + # Create the first line of the voucher - move_line_id = move_line_pool.create(cr, uid, self.first_move_line_get(cr,uid,voucher.id, move_id, company_currency, current_currency, local_context), local_context) + move_line_id = move_line_pool.create(cr, uid, self.first_move_line_get(cr,uid,voucher.id, move_id, company_currency, current_currency, context), context) move_line_brw = move_line_pool.browse(cr, uid, move_line_id, context=context) line_total = move_line_brw.debit - move_line_brw.credit + if voucher.type in ('sale', 'receipt'): + if voucher.hopdong_id: + hopdong_id=voucher.hopdong_id.id + #tong_tien_hinhthuc=voucher.tong_tien_hinhthuc + ngay_batdau=voucher.date + ngay_ketthuc=voucher.date + if hopdong_id: + dambao=False + from datetime import datetime + tn=datetime.strptime(ngay_batdau,'%Y-%m-%d') + today =time.strftime('%Y-%m-%d') + td=datetime.strptime(today,'%Y-%m-%d') + if tn>= td: + dambao=True + hinhthuc_id1=self.pool.get('icsc.hinhthuc').create(cr, uid, {'name':name or 'Thanh toán cho khách hàng','amount':line_total}, context) + self.pool.get('icsc.hopdong.banhang.hinhthuc').create(cr, uid, {'name':hinhthuc_id1,'amount':line_total,'hop_dong_id':hopdong_id,'ngay_batdau':ngay_batdau,'dam_bao':dambao}, context) + rec_list_ids = [] if voucher.type == 'sale': line_total = line_total - self._convert_amount(cr, uid, voucher.tax_amount, voucher.id, context=ctx) elif voucher.type == 'purchase': line_total = line_total + self._convert_amount(cr, uid, voucher.tax_amount, voucher.id, context=ctx) + # Create one move line per voucher line where amount is not 0.0 line_total, rec_list_ids = self.voucher_move_line_create(cr, uid, voucher.id, line_total, move_id, company_currency, current_currency, context) # Create the writeoff line if needed - ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, local_context) + ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, context) if ml_writeoff: - move_line_pool.create(cr, uid, ml_writeoff, local_context) + move_line_pool.create(cr, uid, ml_writeoff, context) # We post the voucher. self.write(cr, uid, [voucher.id], { 'move_id': move_id, @@ -1536,7 +1533,7 @@ def _currency_id(self, cr, uid, ids, name, args, context=None): else: res[line.id] = line.voucher_id.currency_id and line.voucher_id.currency_id.id or line.voucher_id.company_id.currency_id.id return res - + _columns = { 'voucher_id':fields.many2one('account.voucher', 'Voucher', required=1, ondelete='cascade'), 'name':fields.char('Description', size=256), @@ -1668,11 +1665,7 @@ def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, nex bank_st_line_obj = self.pool.get('account.bank.statement.line') st_line = bank_st_line_obj.browse(cr, uid, st_line_id, context=context) if st_line.voucher_id: - voucher_obj.write(cr, uid, [st_line.voucher_id.id], - {'number': next_number, - 'date': st_line.date, - 'period_id': st_line.statement_id.period_id.id}, - context=context) + voucher_obj.write(cr, uid, [st_line.voucher_id.id], {'number': next_number}, context=context) if st_line.voucher_id.state == 'cancel': voucher_obj.action_cancel_draft(cr, uid, [st_line.voucher_id.id], context=context) wf_service.trg_validate(uid, 'account.voucher', st_line.voucher_id.id, 'proforma_voucher', cr) @@ -1723,7 +1716,7 @@ def _amount_reconciled(self, cursor, user, ids, name, args, context=None): def _check_amount(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): if obj.voucher_id: - diff = abs(obj.amount) - abs(obj.voucher_id.amount) + diff = abs(obj.amount) - obj.voucher_id.amount if not self.pool.get('res.currency').is_zero(cr, uid, obj.statement_id.currency, diff): return False return True diff --git a/addons/account_voucher/account_voucher_data.xml b/addons/account_voucher/account_voucher_data.xml index 6329176c6e9ad..9f8b91a152e7d 100644 --- a/addons/account_voucher/account_voucher_data.xml +++ b/addons/account_voucher/account_voucher_data.xml @@ -2,7 +2,7 @@ - + diff --git a/addons/account_voucher/account_voucher_pay_invoice.xml b/addons/account_voucher/account_voucher_pay_invoice.xml index 284632b9b5fe7..3631829d6baf5 100644 --- a/addons/account_voucher/account_voucher_pay_invoice.xml +++ b/addons/account_voucher/account_voucher_pay_invoice.xml @@ -1,19 +1,19 @@ - - account.invoice.customer.pay - account.invoice - - - -

    - A password reset has been requested for this user. An email containing the following link has been sent: + Một thiết lập lại mật khẩu đã được yêu cầu bởi người dùng. Một email có chứa liên kết sau đây đã được gửi đi:

    - An invitation email containing the following subscription link has been sent: + Một email có chứa các lời mời liên kết đăng ký dưới đây đã được gửi đi:

    @@ -31,10 +31,10 @@
    -
    diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 13f2184132fd8..9df80d5e42088 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -39,6 +39,12 @@ openerp.auth_signup = function(instance) { delete self.params.error_message; } + // in case of a signup, retrieve the user information from the token + if (dbname && self.params.token) { + self.rpc("/auth_signup/retrieve", {dbname: dbname, token: self.params.token}) + .done(self.on_token_loaded) + .fail(self.on_token_failed); + } if (dbname && self.params.login) { self.$("form input[name=login]").val(self.params.login); } @@ -47,7 +53,7 @@ openerp.auth_signup = function(instance) { self.$('a.oe_signup_reset_password').click(self.do_reset_password); if (dbname) { - self.rpc("/auth_signup/get_config", {dbname: dbname}).then(function(result) { + self.rpc("/auth_signup/get_config", {dbname: dbname}).done(function(result) { self.signup_enabled = result.signup; self.reset_password_enabled = result.reset_password; if (!self.signup_enabled || self.$("form input[name=login]").val()){ @@ -55,13 +61,6 @@ openerp.auth_signup = function(instance) { } else { self.set('login_mode', 'signup'); } - - // in case of a signup, retrieve the user information from the token - if (self.params.token) { - self.rpc("/auth_signup/retrieve", {dbname: dbname, token: self.params.token}) - .then(self.on_token_loaded, self.on_token_failed); - } - }); } else { // TODO: support multiple database mode diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 797ca5b58d6ed..a5ac0ec2a25a8 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -1,288 +1,217 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from datetime import datetime -from dateutil.relativedelta import relativedelta -import time -import logging - -import openerp -from openerp import SUPERUSER_ID -from openerp.osv import fields, osv -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools.safe_eval import safe_eval as eval - -_logger = logging.getLogger(__name__) - -DATE_RANGE_FUNCTION = { - 'minutes': lambda interval: relativedelta(minutes=interval), - 'hour': lambda interval: relativedelta(hours=interval), - 'day': lambda interval: relativedelta(days=interval), - 'month': lambda interval: relativedelta(months=interval), - False: lambda interval: relativedelta(0), -} - -def get_datetime(date_str): - '''Return a datetime from a date string or a datetime string''' - # complete date time if date_str contains only a date - if ' ' not in date_str: - date_str = date_str + " 00:00:00" - return datetime.strptime(date_str, DEFAULT_SERVER_DATETIME_FORMAT) - - -class base_action_rule(osv.osv): - """ Base Action Rules """ - - _name = 'base.action.rule' - _description = 'Action Rules' - - _columns = { - 'name': fields.char('Rule Name', size=64, required=True), - 'model_id': fields.many2one('ir.model', 'Related Document Model', - required=True, domain=[('osv_memory', '=', False)]), - 'model': fields.related('model_id', 'model', type="char", size=256, string='Model'), - 'create_date': fields.datetime('Create Date', readonly=1), - 'active': fields.boolean('Active', - help="When unchecked, the rule is hidden and will not be executed."), - 'sequence': fields.integer('Sequence', - help="Gives the sequence order when displaying a list of rules."), - 'trg_date_id': fields.many2one('ir.model.fields', string='Trigger Date', - help="When should the condition be triggered. If present, will be checked by the scheduler. If empty, will be checked at creation and update.", - domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]"), - 'trg_date_range': fields.integer('Delay after trigger date', - help="Delay after the trigger date." \ - "You can put a negative number if you need a delay before the" \ - "trigger date, like sending a reminder 15 minutes before a meeting."), - 'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'), - ('day', 'Days'), ('month', 'Months')], 'Delay type'), - 'act_user_id': fields.many2one('res.users', 'Set Responsible'), - 'act_followers': fields.many2many("res.partner", string="Add Followers"), - 'server_action_ids': fields.many2many('ir.actions.server', string='Server Actions', - domain="[('model_id', '=', model_id)]", - help="Examples: email reminders, call object service, etc."), - 'filter_pre_id': fields.many2one('ir.filters', string='Before Update Filter', - ondelete='restrict', - domain="[('model_id', '=', model_id.model)]", - help="If present, this condition must be satisfied before the update of the record."), - 'filter_id': fields.many2one('ir.filters', string='After Update Filter', - ondelete='restrict', - domain="[('model_id', '=', model_id.model)]", - help="If present, this condition must be satisfied after the update of the record."), - 'last_run': fields.datetime('Last Run', readonly=1), - } - - _defaults = { - 'active': True, - 'trg_date_range_type': 'day', - } - - _order = 'sequence' - - def _filter(self, cr, uid, action, action_filter, record_ids, context=None): - """ filter the list record_ids that satisfy the action filter """ - if record_ids and action_filter: - assert action.model == action_filter.model_id, "Filter model different from action rule model" - model = self.pool.get(action_filter.model_id) - domain = [('id', 'in', record_ids)] + eval(action_filter.domain) - ctx = dict(context or {}) - ctx.update(eval(action_filter.context)) - record_ids = model.search(cr, uid, domain, context=ctx) - return record_ids - - def _process(self, cr, uid, action, record_ids, context=None): - """ process the given action on the records """ - # execute server actions - model = self.pool.get(action.model_id.model) - if action.server_action_ids: - server_action_ids = map(int, action.server_action_ids) - for record in model.browse(cr, uid, record_ids, context): - action_server_obj = self.pool.get('ir.actions.server') - ctx = dict(context, active_model=model._name, active_ids=[record.id], active_id=record.id) - action_server_obj.run(cr, uid, server_action_ids, context=ctx) - - # modify records - values = {} - if 'date_action_last' in model._all_columns: - values['date_action_last'] = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - if action.act_user_id and 'user_id' in model._all_columns: - values['user_id'] = action.act_user_id.id - if values: - model.write(cr, uid, record_ids, values, context=context) - - if action.act_followers and hasattr(model, 'message_subscribe'): - follower_ids = map(int, action.act_followers) - model.message_subscribe(cr, uid, record_ids, follower_ids, context=context) - - return True - - def _wrap_create(self, old_create, model): - """ Return a wrapper around `old_create` calling both `old_create` and - `_process`, in that order. - """ - def wrapper(cr, uid, vals, context=None, **kwargs): - # avoid loops or cascading actions - if context and context.get('action'): - return old_create(cr, uid, vals, context=context) - - context = dict(context or {}, action=True) - new_id = old_create(cr, uid, vals, context=context, **kwargs) - - # as it is a new record, we do not consider the actions that have a prefilter - action_dom = [('model', '=', model), ('trg_date_id', '=', False), ('filter_pre_id', '=', False)] - action_ids = self.search(cr, uid, action_dom, context=context) - - # check postconditions, and execute actions on the records that satisfy them - for action in self.browse(cr, uid, action_ids, context=context): - if self._filter(cr, uid, action, action.filter_id, [new_id], context=context): - self._process(cr, uid, action, [new_id], context=context) - return new_id - - return wrapper - - def _wrap_write(self, old_write, model): - """ Return a wrapper around `old_write` calling both `old_write` and - `_process`, in that order. - """ - def wrapper(cr, uid, ids, vals, context=None, **kwargs): - # avoid loops or cascading actions - if context and context.get('action'): - return old_write(cr, uid, ids, vals, context=context, **kwargs) - - context = dict(context or {}, action=True) - ids = [ids] if isinstance(ids, (int, long, str)) else ids - - # retrieve the action rules to possibly execute - action_dom = [('model', '=', model), ('trg_date_id', '=', False)] - action_ids = self.search(cr, uid, action_dom, context=context) - actions = self.browse(cr, uid, action_ids, context=context) - - # check preconditions - pre_ids = {} - for action in actions: - pre_ids[action] = self._filter(cr, uid, action, action.filter_pre_id, ids, context=context) - - # execute write - old_write(cr, uid, ids, vals, context=context, **kwargs) - - # check postconditions, and execute actions on the records that satisfy them - for action in actions: - post_ids = self._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context) - if post_ids: - self._process(cr, uid, action, post_ids, context=context) - return True - - return wrapper - - def _register_hook(self, cr, ids=None): - """ Wrap the methods `create` and `write` of the models specified by - the rules given by `ids` (or all existing rules if `ids` is `None`.) - """ - updated = False - if ids is None: - ids = self.search(cr, SUPERUSER_ID, []) - for action_rule in self.browse(cr, SUPERUSER_ID, ids): - model = action_rule.model_id.model - model_obj = self.pool.get(model) - if model_obj and not hasattr(model_obj, 'base_action_ruled'): - model_obj.create = self._wrap_create(model_obj.create, model) - model_obj.write = self._wrap_write(model_obj.write, model) - model_obj.base_action_ruled = True - updated = True - return updated - - def create(self, cr, uid, vals, context=None): - res_id = super(base_action_rule, self).create(cr, uid, vals, context=context) - if self._register_hook(cr, [res_id]): - openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname) - return res_id - - def write(self, cr, uid, ids, vals, context=None): - if isinstance(ids, (int, long)): - ids = [ids] - super(base_action_rule, self).write(cr, uid, ids, vals, context=context) - if self._register_hook(cr, ids): - openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname) - return True - - def onchange_model_id(self, cr, uid, ids, model_id, context=None): - data = {'model': False, 'filter_pre_id': False, 'filter_id': False} - if model_id: - model = self.pool.get('ir.model').browse(cr, uid, model_id, context=context) - data.update({'model': model.model}) - return {'value': data} - - def _check(self, cr, uid, automatic=False, use_new_cursor=False, context=None): - """ This Function is called by scheduler. """ - context = context or {} - # retrieve all the action rules that have a trg_date_id and no precondition - action_dom = [('trg_date_id', '!=', False), ('filter_pre_id', '=', False)] - action_ids = self.search(cr, uid, action_dom, context=context) - for action in self.browse(cr, uid, action_ids, context=context): - now = datetime.now() - if action.last_run: - last_run = get_datetime(action.last_run) - else: - last_run = datetime.utcfromtimestamp(0) - - # retrieve all the records that satisfy the action's condition - model = self.pool.get(action.model_id.model) - domain = [] - ctx = dict(context) - if action.filter_id: - domain = eval(action.filter_id.domain) - ctx.update(eval(action.filter_id.context)) - if 'lang' not in ctx: - # Filters might be language-sensitive, attempt to reuse creator lang - # as we are usually running this as super-user in background - [filter_meta] = action.filter_id.perm_read() - user_id = filter_meta['write_uid'] and filter_meta['write_uid'][0] or \ - filter_meta['create_uid'][0] - ctx['lang'] = self.pool['res.users'].browse(cr, uid, user_id).lang - record_ids = model.search(cr, uid, domain, context=ctx) - - # determine when action should occur for the records - date_field = action.trg_date_id.name - if date_field == 'date_action_last' and 'create_date' in model._all_columns: - get_record_dt = lambda record: record[date_field] or record.create_date - else: - get_record_dt = lambda record: record[date_field] - - delay = DATE_RANGE_FUNCTION[action.trg_date_range_type](action.trg_date_range) - - # process action on the records that should be executed - for record in model.browse(cr, uid, record_ids, context=context): - record_dt = get_record_dt(record) - if not record_dt: - continue - action_dt = get_datetime(record_dt) + delay - if last_run <= action_dt < now: - try: - context = dict(context or {}, action=True) - self._process(cr, uid, action, [record.id], context=context) - except Exception: - import traceback - _logger.error(traceback.format_exc()) - - action.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) - - if automatic: - # auto-commit for batch processing - cr.commit() +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from datetime import datetime, timedelta +import time +import logging +from openerp import SUPERUSER_ID +from openerp.osv import fields, osv +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT +_logger = logging.getLogger(__name__) +DATE_RANGE_FUNCTION = {'minutes': lambda interval: timedelta(minutes=interval), + 'hour': lambda interval: timedelta(hours=interval), + 'day': lambda interval: timedelta(days=interval), + 'month': lambda interval: timedelta(months=interval), + False: lambda interval: timedelta(0)} + +def get_datetime(date_str): + """Return a datetime from a date string or a datetime string""" + if ' ' not in date_str: + date_str = date_str + ' 00:00:00' + return datetime.strptime(date_str, DEFAULT_SERVER_DATETIME_FORMAT) + + +class base_action_rule(osv.osv): + """ Base Action Rules """ + _name = 'base.action.rule' + _description = 'Action Rules' + _columns = {'name': fields.char('Rule Name', size=64, required=True), + 'model_id': fields.many2one('ir.model', 'Related Document Model', required=True, domain=[('osv_memory', '=', False)]), + 'model': fields.related('model_id', 'model', type='char', size=256, string='Model'), + 'create_date': fields.datetime('Create Date', readonly=1), + 'active': fields.boolean('Active', help='When unchecked, the rule is hidden and will not be executed.'), + 'sequence': fields.integer('Sequence', help='Gives the sequence order when displaying a list of rules.'), + 'trg_date_id': fields.many2one('ir.model.fields', string='Trigger Date', help='When should the condition be triggered. If present, will be checked by the scheduler. If empty, will be checked at creation and update.', domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]"), + 'trg_date_range': fields.integer('Delay after trigger date', help='Delay after the trigger date.You can put a negative number if you need a delay before thetrigger date, like sending a reminder 15 minutes before a meeting.'), + 'trg_date_range_type': fields.selection([('minutes', 'Minutes'), + ('hour', 'Hours'), + ('day', 'Days'), + ('month', 'Months')], 'Delay type'), + 'act_user_id': fields.many2one('res.users', 'Set Responsible'), + 'act_followers': fields.many2many('res.partner', string='Add Followers'), + 'server_action_ids': fields.many2many('ir.actions.server', string='Server Actions', domain="[('model_id', '=', model_id)]", help='Examples: email reminders, call object service, etc.'), + 'filter_pre_id': fields.many2one('ir.filters', string='Before Update Filter', ondelete='restrict', domain="[('model_id', '=', model_id.model)]", help='If present, this condition must be satisfied before the update of the record.'), + 'filter_id': fields.many2one('ir.filters', string='After Update Filter', ondelete='restrict', domain="[('model_id', '=', model_id.model)]", help='If present, this condition must be satisfied after the update of the record.'), + 'last_run': fields.datetime('Last Run', readonly=1)} + _defaults = {'active': True, + 'trg_date_range_type': 'day'} + _order = 'sequence' + + def _filter(self, cr, uid, action, action_filter, record_ids, context = None): + """ filter the list record_ids that satisfy the action filter """ + if not (record_ids and action_filter and action.model == action_filter.model_id): + raise AssertionError('Filter model different from action rule model') + model = self.pool.get(action_filter.model_id) + domain = [('id', 'in', record_ids)] + eval(action_filter.domain) + ctx = dict(context or {}) + ctx.update(eval(action_filter.context)) + record_ids = model.search(cr, uid, domain, context=ctx) + return record_ids + + def _process(self, cr, uid, action, record_ids, context = None): + """ process the given action on the records """ + model = self.pool.get(action.model_id.model) + if action.server_action_ids: + server_action_ids = map(int, action.server_action_ids) + for record in model.browse(cr, uid, record_ids, context): + action_server_obj = self.pool.get('ir.actions.server') + ctx = dict(context, active_model=model._name, active_ids=[record.id], active_id=record.id) + action_server_obj.run(cr, uid, server_action_ids, context=ctx) + + values = {} + if 'date_action_last' in model._all_columns: + values['date_action_last'] = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + if action.act_user_id and 'user_id' in model._all_columns: + values['user_id'] = action.act_user_id.id + if values: + model.write(cr, uid, record_ids, values, context=context) + if action.act_followers and hasattr(model, 'message_subscribe'): + follower_ids = map(int, action.act_followers) + model.message_subscribe(cr, uid, record_ids, follower_ids, context=context) + return True + + def _wrap_create(self, old_create, model): + """ Return a wrapper around `old_create` calling both `old_create` and + `_process`, in that order. + """ + + def wrapper(cr, uid, vals, context = None): + if context and context.get('action'): + return old_create(cr, uid, vals, context=context) + context = dict(context or {}, action=True) + new_id = old_create(cr, uid, vals, context=context) + action_dom = [('model', '=', model), ('trg_date_id', '=', False), ('filter_pre_id', '=', False)] + action_ids = self.search(cr, uid, action_dom, context=context) + for action in self.browse(cr, uid, action_ids, context=context): + if self._filter(cr, uid, action, action.filter_id, [new_id], context=context): + self._process(cr, uid, action, [new_id], context=context) + + return new_id + + return wrapper + + def _wrap_write(self, old_write, model): + """ Return a wrapper around `old_write` calling both `old_write` and + `_process`, in that order. + """ + + def wrapper(cr, uid, ids, vals, context = None): + if context and context.get('action'): + return old_write(cr, uid, ids, vals, context=context) + context = dict(context or {}, action=True) + ids = [ids] if isinstance(ids, (int, long, str)) else ids + action_dom = [('model', '=', model), ('trg_date_id', '=', False)] + action_ids = self.search(cr, uid, action_dom, context=context) + actions = self.browse(cr, uid, action_ids, context=context) + pre_ids = {} + for action in actions: + pre_ids[action] = self._filter(cr, uid, action, action.filter_pre_id, ids, context=context) + + old_write(cr, uid, ids, vals, context=context) + for action in actions: + post_ids = self._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context) + if post_ids: + self._process(cr, uid, action, post_ids, context=context) + + return True + + return wrapper + + def _register_hook(self, cr, ids = None): + """ Wrap the methods `create` and `write` of the models specified by + the rules given by `ids` (or all existing rules if `ids` is `None`.) + """ + if ids is None: + ids = self.search(cr, SUPERUSER_ID, []) + for action_rule in self.browse(cr, SUPERUSER_ID, ids): + model = action_rule.model_id.model + model_obj = self.pool.get(model) + if not hasattr(model_obj, 'base_action_ruled'): + model_obj.create = self._wrap_create(model_obj.create, model) + model_obj.write = self._wrap_write(model_obj.write, model) + model_obj.base_action_ruled = True + + return True + + def create(self, cr, uid, vals, context = None): + res_id = super(base_action_rule, self).create(cr, uid, vals, context=context) + self._register_hook(cr, [res_id]) + return res_id + + def write(self, cr, uid, ids, vals, context = None): + if isinstance(ids, (int, long)): + ids = [ids] + super(base_action_rule, self).write(cr, uid, ids, vals, context=context) + self._register_hook(cr, ids) + return True + + def onchange_model_id(self, cr, uid, ids, model_id, context = None): + data = {'model': False, + 'filter_pre_id': False, + 'filter_id': False} + if model_id: + model = self.pool.get('ir.model').browse(cr, uid, model_id, context=context) + data.update({'model': model.model}) + return {'value': data} + + def _check(self, cr, uid, automatic = False, use_new_cursor = False, context = None): + """ This Function is called by scheduler. """ + context = context or {} + action_dom = [('trg_date_id', '!=', False), ('filter_pre_id', '=', False)] + action_ids = self.search(cr, uid, action_dom, context=context) + for action in self.browse(cr, uid, action_ids, context=context): + now = datetime.now() + last_run = get_datetime(action.last_run) if action.last_run else False + model = self.pool.get(action.model_id.model) + domain = [] + ctx = dict(context) + if action.filter_id: + domain = eval(action.filter_id.domain) + ctx.update(eval(action.filter_id.context)) + record_ids = model.search(cr, uid, domain, context=ctx) + date_field = action.trg_date_id.name + if date_field == 'date_action_last' and 'create_date' in model._all_columns: + get_record_dt = lambda record: record[date_field] or record.create_date + else: + get_record_dt = lambda record: record[date_field] + delay = DATE_RANGE_FUNCTION[action.trg_date_range_type](action.trg_date_range) + for record in model.browse(cr, uid, record_ids, context=context): + record_dt = get_record_dt(record) + if not record_dt: + continue + action_dt = get_datetime(record_dt) + delay + if last_run and last_run <= action_dt < now or action_dt < now: + try: + self._process(cr, uid, action, [record.id], context=context) + except Exception: + import traceback + _logger.error(traceback.format_exc()) + + action.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) \ No newline at end of file diff --git a/addons/base_action_rule/base_action_rule_data.xml b/addons/base_action_rule/base_action_rule_data.xml index d4b12f5d4a3ef..7a9aabedf5f58 100644 --- a/addons/base_action_rule/base_action_rule_data.xml +++ b/addons/base_action_rule/base_action_rule_data.xml @@ -10,7 +10,7 @@ - + diff --git a/addons/base_action_rule/images/base_action_rule1.jpeg b/addons/base_action_rule/images/base_action_rule1.jpeg new file mode 100644 index 0000000000000..1cd4e9339bd22 Binary files /dev/null and b/addons/base_action_rule/images/base_action_rule1.jpeg differ diff --git a/addons/base_action_rule/images/base_action_rule2.jpeg b/addons/base_action_rule/images/base_action_rule2.jpeg new file mode 100644 index 0000000000000..9ff245ebc1199 Binary files /dev/null and b/addons/base_action_rule/images/base_action_rule2.jpeg differ diff --git a/addons/base_action_rule/images/base_action_rule3.jpeg b/addons/base_action_rule/images/base_action_rule3.jpeg new file mode 100644 index 0000000000000..659f2e98022a3 Binary files /dev/null and b/addons/base_action_rule/images/base_action_rule3.jpeg differ diff --git a/addons/base_action_rule/security/ir.model.access.csv b/addons/base_action_rule/security/ir.model.access.csv index b509ec629442c..92d3d55649f91 100644 --- a/addons/base_action_rule/security/ir.model.access.csv +++ b/addons/base_action_rule/security/ir.model.access.csv @@ -1,4 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_base_action_rule,base.action.rule,model_base_action_rule,,1,0,0,0 access_base_action_rule_config,base.action.rule config,model_base_action_rule,base.group_system,1,1,1,1 -access_base_action_rule_lead_test,access_base_action_rule_lead_test,model_base_action_rule_lead_test,base.group_system,1,1,1,1 diff --git a/addons/base_action_rule/test_models.py b/addons/base_action_rule/test_models.py index 9469b42c70548..aea25de6412dd 100644 --- a/addons/base_action_rule/test_models.py +++ b/addons/base_action_rule/test_models.py @@ -1,32 +1,43 @@ -from osv import osv, fields - -AVAILABLE_STATES = [ - ('draft', 'New'), - ('cancel', 'Cancelled'), - ('open', 'In Progress'), - ('pending', 'Pending'), - ('done', 'Closed') -] - -class lead_test(osv.Model): - _name = "base.action.rule.lead.test" - - _columns = { - 'name': fields.char('Subject', size=64, required=True, select=1), - 'user_id': fields.many2one('res.users', 'Responsible'), - 'state': fields.selection(AVAILABLE_STATES, string="Status", readonly=True), - 'active': fields.boolean('Active', required=False), - 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null'), - 'date_action_last': fields.datetime('Last Action', readonly=1), - } - - _defaults = { - 'state' : 'draft', - 'active' : True, - } - - def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs): - pass - - def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None): - pass +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from osv import osv, fields +AVAILABLE_STATES = [('draft', 'New'), + ('cancel', 'Cancelled'), + ('open', 'In Progress'), + ('pending', 'Pending'), + ('done', 'Closed')] + +class lead_test(osv.Model): + _name = 'base.action.rule.lead.test' + _columns = {'name': fields.char('Subject', size=64, required=True, select=1), + 'user_id': fields.many2one('res.users', 'Responsible'), + 'state': fields.selection(AVAILABLE_STATES, string='Status', readonly=True), + 'active': fields.boolean('Active', required=False), + 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null'), + 'date_action_last': fields.datetime('Last Action', readonly=1)} + _defaults = {'state': 'draft', + 'active': True} + + def message_post(self, cr, uid, thread_id, body = '', subject = None, type = 'notification', subtype = None, parent_id = False, attachments = None, context = None, **kwargs): + pass + + def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids = None, context = None): + pass \ No newline at end of file diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml index 84f9ea4355db2..4b6407c0de6d5 100644 --- a/addons/base_calendar/base_calendar_view.xml +++ b/addons/base_calendar/base_calendar_view.xml @@ -7,11 +7,12 @@ parent="base.menu_base_config" sequence="50" groups="base.group_no_one"/> - + @@ -61,8 +67,7 @@ action="base_calendar.action_res_alarm_view" parent="base.menu_calendar_configuration" sequence="5"/> - - + diff --git a/addons/base_calendar/images/base_calendar1.jpeg b/addons/base_calendar/images/base_calendar1.jpeg new file mode 100644 index 0000000000000..29def1da9267a Binary files /dev/null and b/addons/base_calendar/images/base_calendar1.jpeg differ diff --git a/addons/base_calendar/images/base_calendar2.jpeg b/addons/base_calendar/images/base_calendar2.jpeg new file mode 100644 index 0000000000000..c210239b82def Binary files /dev/null and b/addons/base_calendar/images/base_calendar2.jpeg differ diff --git a/addons/base_calendar/images/base_calendar3.jpeg b/addons/base_calendar/images/base_calendar3.jpeg new file mode 100644 index 0000000000000..d9ce54d2e6ea2 Binary files /dev/null and b/addons/base_calendar/images/base_calendar3.jpeg differ diff --git a/addons/base_calendar/images/base_calendar4.jpeg b/addons/base_calendar/images/base_calendar4.jpeg new file mode 100644 index 0000000000000..83e3b3ae7640a Binary files /dev/null and b/addons/base_calendar/images/base_calendar4.jpeg differ diff --git a/addons/base_calendar/test/base_calendar_test.yml b/addons/base_calendar/test/base_calendar_test.yml index c280bfe298a03..7afd2e49551e2 100644 --- a/addons/base_calendar/test/base_calendar_test.yml +++ b/addons/base_calendar/test/base_calendar_test.yml @@ -52,20 +52,3 @@ - !python {model: calendar.event}: | self.write(cr, uid, [ref("calendar_event_alldaytestevent0")], {'alarm_id': ref("res_alarm_daybeforeeventstarts0")}) -- - I create a recuring rule for my event -- - !record {model: crm.meeting, id: crm_meeting_sprintreview1}: - name: Begin of month meeting - date: !eval time.strftime('%Y-%m-%d 12:00:00') - recurrency: true - rrule: FREQ=MONTHLY;INTERVAL=1;COUNT=12;BYDAY=1MO -- - I check that the attributes are set correctly -- - !assert {model: crm.meeting, id: crm_meeting_sprintreview1}: - - rrule_type == 'monthly' - - count == 12 - - select1 == 'day' - - byday == '1' - - week_list == 'MO' diff --git a/addons/base_iban/base_iban.py b/addons/base_iban/base_iban.py index 0872af2d0f366..369a3eb9be584 100644 --- a/addons/base_iban/base_iban.py +++ b/addons/base_iban/base_iban.py @@ -1,186 +1,196 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -import string - -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -# Reference Examples of IBAN -_ref_iban = { 'al':'ALkk BBBS SSSK CCCC CCCC CCCC CCCC', 'ad':'ADkk BBBB SSSS CCCC CCCC CCCC', -'at':'ATkk BBBB BCCC CCCC CCCC', 'be': 'BEkk BBBC CCCC CCKK', 'ba': 'BAkk BBBS SSCC CCCC CCKK', -'bg': 'BGkk BBBB SSSS DDCC CCCC CC', 'bh': 'BHkk BBBB SSSS SSSS SSSS SS', -'cr': 'CRkk BBBC CCCC CCCC CCCC C', -'hr': 'HRkk BBBB BBBC CCCC CCCC C', 'cy': 'CYkk BBBS SSSS CCCC CCCC CCCC CCCC', -'cz': 'CZkk BBBB SSSS SSCC CCCC CCCC', 'dk': 'DKkk BBBB CCCC CCCC CC', -'do': 'DOkk BBBB CCCC CCCC CCCC CCCC CCCC', - 'ee': 'EEkk BBSS CCCC CCCC CCCK', 'fo': 'FOkk CCCC CCCC CCCC CC', - 'fi': 'FIkk BBBB BBCC CCCC CK', 'fr': 'FRkk BBBB BGGG GGCC CCCC CCCC CKK', - 'ge': 'GEkk BBCC CCCC CCCC CCCC CC', 'de': 'DEkk BBBB BBBB CCCC CCCC CC', - 'gi': 'GIkk BBBB CCCC CCCC CCCC CCC', 'gr': 'GRkk BBBS SSSC CCCC CCCC CCCC CCC', - 'gl': 'GLkk BBBB CCCC CCCC CC', 'hu': 'HUkk BBBS SSSC CCCC CCCC CCCC CCCC', - 'is':'ISkk BBBB SSCC CCCC XXXX XXXX XX', 'ie': 'IEkk BBBB SSSS SSCC CCCC CC', - 'il': 'ILkk BBBS SSCC CCCC CCCC CCC', 'it': 'ITkk KBBB BBSS SSSC CCCC CCCC CCC', - 'kz': 'KZkk BBBC CCCC CCCC CCCC', 'kw': 'KWkk BBBB CCCC CCCC CCCC CCCC CCCC CC', - 'lv': 'LVkk BBBB CCCC CCCC CCCC C', -'lb': 'LBkk BBBB CCCC CCCC CCCC CCCC CCCC', 'li': 'LIkk BBBB BCCC CCCC CCCC C', -'lt': 'LTkk BBBB BCCC CCCC CCCC', 'lu': 'LUkk BBBC CCCC CCCC CCCC' , -'mk': 'MKkk BBBC CCCC CCCC CKK', 'mt': 'MTkk BBBB SSSS SCCC CCCC CCCC CCCC CCC', -'mr': 'MRkk BBBB BSSS SSCC CCCC CCCC CKK', -'mu': 'MUkk BBBB BBSS CCCC CCCC CCCC CCCC CC', 'mc': 'MCkk BBBB BGGG GGCC CCCC CCCC CKK', -'me': 'MEkk BBBC CCCC CCCC CCCC KK', -'nl': 'NLkk BBBB CCCC CCCC CC', 'no': 'NOkk BBBB CCCC CCK', -'pl':'PLkk BBBS SSSK CCCC CCCC CCCC CCCC', -'pt': 'PTkk BBBB SSSS CCCC CCCC CCCK K', 'ro': 'ROkk BBBB CCCC CCCC CCCC CCCC', -'sm': 'SMkk KBBB BBSS SSSC CCCC CCCC CCC', 'sa': 'SAkk BBCC CCCC CCCC CCCC CCCC', -'rs': 'RSkk BBBC CCCC CCCC CCCC KK', 'sk': 'SKkk BBBB SSSS SSCC CCCC CCCC', -'si': 'SIkk BBSS SCCC CCCC CKK', 'es': 'ESkk BBBB SSSS KKCC CCCC CCCC', -'se': 'SEkk BBBB CCCC CCCC CCCC CCCC', 'ch': 'CHkk BBBB BCCC CCCC CCCC C', -'tn': 'TNkk BBSS SCCC CCCC CCCC CCCC', 'tr': 'TRkk BBBB BRCC CCCC CCCC CCCC CC', -'ae': 'AEkk BBBC CCCC CCCC CCCC CCC', -'gb': 'GBkk BBBB SSSS SSCC CCCC CC', -} - -def _format_iban(iban_str): - ''' - This function removes all characters from given 'iban_str' that isn't a alpha numeric and converts it to upper case. - ''' - res = "" - if iban_str: - for char in iban_str: - if char.isalnum(): - res += char.upper() - return res - -def _pretty_iban(iban_str): - "return iban_str in groups of four characters separated by a single space" - res = [] - while iban_str: - res.append(iban_str[:4]) - iban_str = iban_str[4:] - return ' '.join(res) - -class res_partner_bank(osv.osv): - _inherit = "res.partner.bank" - - def create(self, cr, uid, vals, context=None): - #overwrite to format the iban number correctly - if (vals.get('state',False)=='iban') and vals.get('acc_number', False): - vals['acc_number'] = _format_iban(vals['acc_number']) - vals['acc_number'] = _pretty_iban(vals['acc_number']) - return super(res_partner_bank, self).create(cr, uid, vals, context) - - def write(self, cr, uid, ids, vals, context=None): - #overwrite to format the iban number correctly - if (vals.get('state',False)=='iban') and vals.get('acc_number', False): - vals['acc_number'] = _format_iban(vals['acc_number']) - vals['acc_number'] = _pretty_iban(vals['acc_number']) - return super(res_partner_bank, self).write(cr, uid, ids, vals, context) - - def is_iban_valid(self, cr, uid, iban, context=None): - """ Check if IBAN is valid or not - @param iban: IBAN as string - @return: True if IBAN is valid, False otherwise - """ - if not iban: - return False - iban = _format_iban(iban).lower() - if iban[:2] in _ref_iban and len(iban) != len(_format_iban(_ref_iban[iban[:2]])): - return False - #the four first digits have to be shifted to the end - iban = iban[4:] + iban[:4] - #letters have to be transformed into numbers (a = 10, b = 11, ...) - iban2 = "" - for char in iban: - if char.isalpha(): - iban2 += str(ord(char)-87) - else: - iban2 += char - #iban is correct if modulo 97 == 1 - return int(iban2) % 97 == 1 - - def check_iban(self, cr, uid, ids, context=None): - ''' - Check the IBAN number - ''' - for bank_acc in self.browse(cr, uid, ids, context=context): - if bank_acc.state != 'iban': - continue - if not self.is_iban_valid(cr, uid, bank_acc.acc_number, context=context): - return False - return True - - def _construct_constraint_msg(self, cr, uid, ids, context=None): - - def default_iban_check(iban_cn): - return iban_cn and iban_cn[0] in string.ascii_lowercase and iban_cn[1] in string.ascii_lowercase - - iban_country = self.browse(cr, uid, ids)[0].acc_number and self.browse(cr, uid, ids)[0].acc_number[:2].lower() - if default_iban_check(iban_country): - if iban_country in _ref_iban: - return _('The IBAN does not seem to be correct. You should have entered something like this %s'), \ - ('%s \nWhere B = National bank code, S = Branch code,'\ - ' C = Account No, K = Check digit' % _ref_iban[iban_country]) - return _('This IBAN does not pass the validation check, please verify it'), () - return _('The IBAN is invalid, it should begin with the country code'), () - - def _check_bank(self, cr, uid, ids, context=None): - for partner_bank in self.browse(cr, uid, ids, context=context): - if partner_bank.state == 'iban' and not partner_bank.bank.bic: - return False - return True - - def get_bban_from_iban(self, cr, uid, ids, context=None): - ''' - This function returns the bank account number computed from the iban account number, thanks to the mapping_list dictionary that contains the rules associated to its country. - ''' - res = {} - mapping_list = { - #TODO add rules for others countries - 'be': lambda x: x[4:], - 'fr': lambda x: x[14:], - 'ch': lambda x: x[9:], - 'gb': lambda x: x[14:], - } - for record in self.browse(cr, uid, ids, context=context): - if not record.acc_number: - res[record.id] = False - continue - res[record.id] = False - for code, function in mapping_list.items(): - if record.acc_number.lower().startswith(code): - res[record.id] = function(record.acc_number) - break - return res - - _columns = { - # Deprecated: we keep it for backward compatibility, to be removed in v7 - # We use acc_number instead of IBAN since v6.1, but we keep this field - # to not break community modules. - 'iban': fields.related('acc_number', string='IBAN', size=34, readonly=True, help="International Bank Account Number", type="char"), - } - _constraints = [ - (check_iban, _construct_constraint_msg, ["iban"]), - (_check_bank, '\nPlease define BIC/Swift code on bank for bank type IBAN Account to make valid payments', ['bic']) - ] - -res_partner_bank() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import string +from openerp.osv import fields, osv +from openerp.tools.translate import _ +_ref_iban = {'al': 'ALkk BBBS SSSK CCCC CCCC CCCC CCCC', + 'ad': 'ADkk BBBB SSSS CCCC CCCC CCCC', + 'at': 'ATkk BBBB BCCC CCCC CCCC', + 'be': 'BEkk BBBC CCCC CCKK', + 'ba': 'BAkk BBBS SSCC CCCC CCKK', + 'bg': 'BGkk BBBB SSSS DDCC CCCC CC', + 'bh': 'BHkk BBBB SSSS SSSS SSSS SS', + 'cr': 'CRkk BBBC CCCC CCCC CCCC C', + 'hr': 'HRkk BBBB BBBC CCCC CCCC C', + 'cy': 'CYkk BBBS SSSS CCCC CCCC CCCC CCCC', + 'cz': 'CZkk BBBB SSSS SSCC CCCC CCCC', + 'dk': 'DKkk BBBB CCCC CCCC CC', + 'do': 'DOkk BBBB CCCC CCCC CCCC CCCC CCCC', + 'ee': 'EEkk BBSS CCCC CCCC CCCK', + 'fo': 'FOkk CCCC CCCC CCCC CC', + 'fi': 'FIkk BBBB BBCC CCCC CK', + 'fr': 'FRkk BBBB BGGG GGCC CCCC CCCC CKK', + 'ge': 'GEkk BBCC CCCC CCCC CCCC CC', + 'de': 'DEkk BBBB BBBB CCCC CCCC CC', + 'gi': 'GIkk BBBB CCCC CCCC CCCC CCC', + 'gr': 'GRkk BBBS SSSC CCCC CCCC CCCC CCC', + 'gl': 'GLkk BBBB CCCC CCCC CC', + 'hu': 'HUkk BBBS SSSC CCCC CCCC CCCC CCCC', + 'is': 'ISkk BBBB SSCC CCCC XXXX XXXX XX', + 'ie': 'IEkk BBBB SSSS SSCC CCCC CC', + 'il': 'ILkk BBBS SSCC CCCC CCCC CCC', + 'it': 'ITkk KBBB BBSS SSSC CCCC CCCC CCC', + 'kz': 'KZkk BBBC CCCC CCCC CCCC', + 'kw': 'KWkk BBBB CCCC CCCC CCCC CCCC CCCC CC', + 'lv': 'LVkk BBBB CCCC CCCC CCCC C', + 'lb': 'LBkk BBBB CCCC CCCC CCCC CCCC CCCC', + 'li': 'LIkk BBBB BCCC CCCC CCCC C', + 'lt': 'LTkk BBBB BCCC CCCC CCCC', + 'lu': 'LUkk BBBC CCCC CCCC CCCC', + 'mk': 'MKkk BBBC CCCC CCCC CKK', + 'mt': 'MTkk BBBB SSSS SCCC CCCC CCCC CCCC CCC', + 'mr': 'MRkk BBBB BSSS SSCC CCCC CCCC CKK', + 'mu': 'MUkk BBBB BBSS CCCC CCCC CCCC CCCC CC', + 'mc': 'MCkk BBBB BGGG GGCC CCCC CCCC CKK', + 'me': 'MEkk BBBC CCCC CCCC CCCC KK', + 'nl': 'NLkk BBBB CCCC CCCC CC', + 'no': 'NOkk BBBB CCCC CCK', + 'pl': 'PLkk BBBS SSSK CCCC CCCC CCCC CCCC', + 'pt': 'PTkk BBBB SSSS CCCC CCCC CCCK K', + 'ro': 'ROkk BBBB CCCC CCCC CCCC CCCC', + 'sm': 'SMkk KBBB BBSS SSSC CCCC CCCC CCC', + 'sa': 'SAkk BBCC CCCC CCCC CCCC CCCC', + 'rs': 'RSkk BBBC CCCC CCCC CCCC KK', + 'sk': 'SKkk BBBB SSSS SSCC CCCC CCCC', + 'si': 'SIkk BBSS SCCC CCCC CKK', + 'es': 'ESkk BBBB SSSS KKCC CCCC CCCC', + 'se': 'SEkk BBBB CCCC CCCC CCCC CCCC', + 'ch': 'CHkk BBBB BCCC CCCC CCCC C', + 'tn': 'TNkk BBSS SCCC CCCC CCCC CCCC', + 'tr': 'TRkk BBBB BRCC CCCC CCCC CCCC CC', + 'ae': 'AEkk BBBC CCCC CCCC CCCC CCC', + 'gb': 'GBkk BBBB SSSS SSCC CCCC CC'} + +def _format_iban(iban_str): + """ + This function removes all characters from given 'iban_str' that isn't a alpha numeric and converts it to upper case. + """ + res = '' + if iban_str: + for char in iban_str: + if char.isalnum(): + res += char.upper() + + return res + + +def _pretty_iban(iban_str): + """return iban_str in groups of four characters separated by a single space""" + res = [] + while iban_str: + res.append(iban_str[:4]) + iban_str = iban_str[4:] + + return ' '.join(res) + + +class res_partner_bank(osv.osv): + _inherit = 'res.partner.bank' + + def create(self, cr, uid, vals, context = None): + if vals.get('state', False) == 'iban' and vals.get('acc_number', False): + vals['acc_number'] = _format_iban(vals['acc_number']) + vals['acc_number'] = _pretty_iban(vals['acc_number']) + return super(res_partner_bank, self).create(cr, uid, vals, context) + + def write(self, cr, uid, ids, vals, context = None): + if vals.get('state', False) == 'iban' and vals.get('acc_number', False): + vals['acc_number'] = _format_iban(vals['acc_number']) + vals['acc_number'] = _pretty_iban(vals['acc_number']) + return super(res_partner_bank, self).write(cr, uid, ids, vals, context) + + def is_iban_valid(self, cr, uid, iban, context = None): + """ Check if IBAN is valid or not + @param iban: IBAN as string + @return: True if IBAN is valid, False otherwise + """ + if not iban: + return False + iban = _format_iban(iban).lower() + if iban[:2] in _ref_iban and len(iban) != len(_format_iban(_ref_iban[iban[:2]])): + return False + iban = iban[4:] + iban[:4] + iban2 = '' + for char in iban: + if char.isalpha(): + iban2 += str(ord(char) - 87) + else: + iban2 += char + + return int(iban2) % 97 == 1 + + def check_iban(self, cr, uid, ids, context = None): + """ + Check the IBAN number + """ + for bank_acc in self.browse(cr, uid, ids, context=context): + if bank_acc.state != 'iban': + continue + if not self.is_iban_valid(cr, uid, bank_acc.acc_number, context=context): + return False + + return True + + def _construct_constraint_msg(self, cr, uid, ids, context = None): + + def default_iban_check(iban_cn): + return iban_cn and iban_cn[0] in string.ascii_lowercase and iban_cn[1] in string.ascii_lowercase + + iban_country = self.browse(cr, uid, ids)[0].acc_number and self.browse(cr, uid, ids)[0].acc_number[:2].lower() + if default_iban_check(iban_country): + if iban_country in _ref_iban: + return (_('The IBAN does not seem to be correct. You should have entered something like this %s'), '%s \nWhere B = National bank code, S = Branch code, C = Account No, K = Check digit' % _ref_iban[iban_country]) + return (_('This IBAN does not pass the validation check, please verify it'), ()) + return (_('The IBAN is invalid, it should begin with the country code'), ()) + + def _check_bank(self, cr, uid, ids, context = None): + for partner_bank in self.browse(cr, uid, ids, context=context): + if partner_bank.state == 'iban' and not partner_bank.bank.bic: + return False + + return True + + def get_bban_from_iban(self, cr, uid, ids, context = None): + """ + This function returns the bank account number computed from the iban account number, thanks to the mapping_list dictionary that contains the rules associated to its country. + """ + res = {} + mapping_list = {'be': lambda x: x[4:], + 'fr': lambda x: x[14:], + 'ch': lambda x: x[9:], + 'gb': lambda x: x[14:]} + for record in self.browse(cr, uid, ids, context=context): + if not record.acc_number: + res[record.id] = False + continue + res[record.id] = False + for code, function in mapping_list.items(): + if record.acc_number.lower().startswith(code): + res[record.id] = function(record.acc_number) + break + + return res + + _columns = {'iban': fields.related('acc_number', string='IBAN', size=34, readonly=True, help='International Bank Account Number', type='char')} + _constraints = [(check_iban, _construct_constraint_msg, ['iban']), (_check_bank, '\nPlease define BIC/Swift code on bank for bank type IBAN Account to make valid payments', ['bic'])] + + +res_partner_bank() \ No newline at end of file diff --git a/addons/base_iban/images/base_iban1.jpeg b/addons/base_iban/images/base_iban1.jpeg new file mode 100644 index 0000000000000..eea3dfb2f771e Binary files /dev/null and b/addons/base_iban/images/base_iban1.jpeg differ diff --git a/addons/base_import/__openerp__.py b/addons/base_import/__openerp__.py index 110d7cd9c27bc..eb8b4561c7ebf 100644 --- a/addons/base_import/__openerp__.py +++ b/addons/base_import/__openerp__.py @@ -26,13 +26,12 @@ 'author': 'OpenERP SA', 'depends': ['web'], 'installable': True, - 'auto_install': True, + 'auto_install': False, 'data': [ 'security/ir.model.access.csv', ], 'css': [ 'static/lib/select2/select2.css', - # TODO for trunk: add select2-bootstrap.css style (to download) 'static/src/css/import.css', ], 'js': [ diff --git a/addons/base_import/controllers.py b/addons/base_import/controllers.py index b0b2ab0714aa5..cf4bdebd7caac 100644 --- a/addons/base_import/controllers.py +++ b/addons/base_import/controllers.py @@ -1,21 +1,33 @@ -# -*- coding: utf-8 -*- -import cgi -import simplejson - -import openerp - -class ImportController(openerp.addons.web.http.Controller): - _cp_path = '/base_import' - - @openerp.addons.web.http.httprequest - def set_file(self, req, file, import_id, jsonp='callback'): - import_id = int(import_id) - - written = req.session.model('base_import.import').write(import_id, { - 'file': file.read(), - 'file_name': file.filename, - 'file_type': file.content_type, - }, req.context) - - return 'window.top.%s(%s)' % ( - cgi.escape(jsonp), simplejson.dumps({'result': written})) +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import simplejson +import openerp + +class ImportController(openerp.addons.web.http.Controller): + _cp_path = '/base_import' + + @openerp.addons.web.http.httprequest + def set_file(self, req, file, import_id, jsonp = 'callback'): + import_id = int(import_id) + written = req.session.model('base_import.import').write(import_id, {'file': file.read(), + 'file_name': file.filename, + 'file_type': file.content_type}, req.context) + return 'window.top.%s(%s)' % (jsonp, simplejson.dumps({'result': written})) \ No newline at end of file diff --git a/addons/base_import/models.py b/addons/base_import/models.py index 637bac01720d9..baf5669fe866c 100644 --- a/addons/base_import/models.py +++ b/addons/base_import/models.py @@ -1,343 +1,276 @@ -import csv -import itertools -import logging -import operator - -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO - -import psycopg2 - -from openerp.osv import orm, fields -from openerp.osv.orm import BaseModel -from openerp.tools.translate import _ - -FIELDS_RECURSION_LIMIT = 2 -ERROR_PREVIEW_BYTES = 200 -_logger = logging.getLogger(__name__) -class ir_import(orm.TransientModel): - _name = 'base_import.import' - # allow imports to survive for 12h in case user is slow - _transient_max_hours = 12.0 - - _columns = { - 'res_model': fields.char('Model', size=64), - 'file': fields.binary( - 'File', help="File to check and/or import, raw binary (not base64)"), - 'file_name': fields.char('File Name', size=None), - 'file_type': fields.char('File Type', size=None), - } - - def get_fields(self, cr, uid, model, context=None, - depth=FIELDS_RECURSION_LIMIT): - """ Recursively get fields for the provided model (through - fields_get) and filter them according to importability - - The output format is a list of ``Field``, with ``Field`` - defined as: - - .. class:: Field - - .. attribute:: id (str) - - A non-unique identifier for the field, used to compute - the span of the ``required`` attribute: if multiple - ``required`` fields have the same id, only one of them - is necessary. - - .. attribute:: name (str) - - The field's logical (OpenERP) name within the scope of - its parent. - - .. attribute:: string (str) - - The field's human-readable name (``@string``) - - .. attribute:: required (bool) - - Whether the field is marked as required in the - model. Clients must provide non-empty import values - for all required fields or the import will error out. - - .. attribute:: fields (list(Field)) - - The current field's subfields. The database and - external identifiers for m2o and m2m fields; a - filtered and transformed fields_get for o2m fields (to - a variable depth defined by ``depth``). - - Fields with no sub-fields will have an empty list of - sub-fields. - - :param str model: name of the model to get fields form - :param int landing: depth of recursion into o2m fields - """ - fields = [{ - 'id': 'id', - 'name': 'id', - 'string': _("External ID"), - 'required': False, - 'fields': [], - }] - fields_got = self.pool[model].fields_get(cr, uid, context=context) - for name, field in fields_got.iteritems(): - # an empty string means the field is deprecated, @deprecated must - # be absent or False to mean not-deprecated - if field.get('deprecated', False) is not False: - continue - if field.get('readonly'): - states = field.get('states') - if not states: - continue - # states = {state: [(attr, value), (attr2, value2)], state2:...} - if not any(attr == 'readonly' and value is False - for attr, value in itertools.chain.from_iterable( - states.itervalues())): - continue - - f = { - 'id': name, - 'name': name, - 'string': field['string'], - # Y U NO ALWAYS HAS REQUIRED - 'required': bool(field.get('required')), - 'fields': [], - } - - if field['type'] in ('many2many', 'many2one'): - f['fields'] = [ - dict(f, name='id', string=_("External ID")), - dict(f, name='.id', string=_("Database ID")), - ] - elif field['type'] == 'one2many' and depth: - f['fields'] = self.get_fields( - cr, uid, field['relation'], context=context, depth=depth-1) - - fields.append(f) - - # TODO: cache on model? - return fields - - def _read_csv(self, record, options): - """ Returns a CSV-parsed iterator of all empty lines in the file - - :throws csv.Error: if an error is detected during CSV parsing - :throws UnicodeDecodeError: if ``options.encoding`` is incorrect - """ - csv_iterator = csv.reader( - StringIO(record.file), - quotechar=str(options['quoting']), - delimiter=str(options['separator'])) - csv_nonempty = itertools.ifilter(None, csv_iterator) - # TODO: guess encoding with chardet? Or https://github.com/aadsm/jschardet - encoding = options.get('encoding', 'utf-8') - return itertools.imap( - lambda row: [item.decode(encoding) for item in row], - csv_nonempty) - - def _match_header(self, header, fields, options): - """ Attempts to match a given header to a field of the - imported model. - - :param str header: header name from the CSV file - :param fields: - :param dict options: - :returns: an empty list if the header couldn't be matched, or - all the fields to traverse - :rtype: list(Field) - """ - for field in fields: - # FIXME: should match all translations & original - # TODO: use string distance (levenshtein? hamming?) - if header == field['name'] \ - or header.lower() == field['string'].lower(): - return [field] - - if '/' not in header: - return [] - - # relational field path - traversal = [] - subfields = fields - # Iteratively dive into fields tree - for section in header.split('/'): - # Strip section in case spaces are added around '/' for - # readability of paths - match = self._match_header(section.strip(), subfields, options) - # Any match failure, exit - if not match: return [] - # prep subfields for next iteration within match[0] - field = match[0] - subfields = field['fields'] - traversal.append(field) - return traversal - - def _match_headers(self, rows, fields, options): - """ Attempts to match the imported model's fields to the - titles of the parsed CSV file, if the file is supposed to have - headers. - - Will consume the first line of the ``rows`` iterator. - - Returns a pair of (None, None) if headers were not requested - or the list of headers and a dict mapping cell indices - to key paths in the ``fields`` tree - - :param Iterator rows: - :param dict fields: - :param dict options: - :rtype: (None, None) | (list(str), dict(int: list(str))) - """ - if not options.get('headers'): - return None, None - - headers = next(rows) - return headers, dict( - (index, [field['name'] for field in self._match_header(header, fields, options)] or None) - for index, header in enumerate(headers) - ) - - def parse_preview(self, cr, uid, id, options, count=10, context=None): - """ Generates a preview of the uploaded files, and performs - fields-matching between the import's file data and the model's - columns. - - If the headers are not requested (not options.headers), - ``matches`` and ``headers`` are both ``False``. - - :param id: identifier of the import - :param int count: number of preview lines to generate - :param options: format-specific options. - CSV: {encoding, quoting, separator, headers} - :type options: {str, str, str, bool} - :returns: {fields, matches, headers, preview} | {error, preview} - :rtype: {dict(str: dict(...)), dict(int, list(str)), list(str), list(list(str))} | {str, str} - """ - (record,) = self.browse(cr, uid, [id], context=context) - fields = self.get_fields(cr, uid, record.res_model, context=context) - - try: - rows = self._read_csv(record, options) - - headers, matches = self._match_headers(rows, fields, options) - # Match should have consumed the first row (iif headers), get - # the ``count`` next rows for preview - preview = itertools.islice(rows, count) - return { - 'fields': fields, - 'matches': matches or False, - 'headers': headers or False, - 'preview': list(preview), - } - except Exception, e: - # Due to lazy generators, UnicodeDecodeError (for - # instance) may only be raised when serializing the - # preview to a list in the return. - _logger.debug("Error during CSV parsing preview", exc_info=True) - return { - 'error': str(e), - # iso-8859-1 ensures decoding will always succeed, - # even if it yields non-printable characters. This is - # in case of UnicodeDecodeError (or csv.Error - # compounded with UnicodeDecodeError) - 'preview': record.file[:ERROR_PREVIEW_BYTES] - .decode( 'iso-8859-1'), - } - - def _convert_import_data(self, record, fields, options, context=None): - """ Extracts the input browse_record and fields list (with - ``False``-y placeholders for fields to *not* import) into a - format Model.import_data can use: a fields list without holes - and the precisely matching data matrix - - :param browse_record record: - :param list(str|bool): fields - :returns: (data, fields) - :rtype: (list(list(str)), list(str)) - :raises ValueError: in case the import data could not be converted - """ - # Get indices for non-empty fields - indices = [index for index, field in enumerate(fields) if field] - if not indices: - raise ValueError(_("You must configure at least one field to import")) - # If only one index, itemgetter will return an atom rather - # than a 1-tuple - if len(indices) == 1: mapper = lambda row: [row[indices[0]]] - else: mapper = operator.itemgetter(*indices) - # Get only list of actually imported fields - import_fields = filter(None, fields) - - rows_to_import = self._read_csv(record, options) - if options.get('headers'): - rows_to_import = itertools.islice( - rows_to_import, 1, None) - data = [ - row for row in itertools.imap(mapper, rows_to_import) - # don't try inserting completely empty rows (e.g. from - # filtering out o2m fields) - if any(row) - ] - - return data, import_fields - - def do(self, cr, uid, id, fields, options, dryrun=False, context=None): - """ Actual execution of the import - - :param fields: import mapping: maps each column to a field, - ``False`` for the columns to ignore - :type fields: list(str|bool) - :param dict options: - :param bool dryrun: performs all import operations (and - validations) but rollbacks writes, allows - getting as much errors as possible without - the risk of clobbering the database. - :returns: A list of errors. If the list is empty the import - executed fully and correctly. If the list is - non-empty it contains dicts with 3 keys ``type`` the - type of error (``error|warning``); ``message`` the - error message associated with the error (a string) - and ``record`` the data which failed to import (or - ``false`` if that data isn't available or provided) - :rtype: list({type, message, record}) - """ - cr.execute('SAVEPOINT import') - - (record,) = self.browse(cr, uid, [id], context=context) - try: - data, import_fields = self._convert_import_data( - record, fields, options, context=context) - except ValueError, e: - return [{ - 'type': 'error', - 'message': unicode(e), - 'record': False, - }] - - _logger.info('importing %d rows...', len(data)) - # DO NOT FORWARD PORT, already fixed in trunk - # hack to avoid to call the load method from ir_translation (name clash) - if record.res_model == 'ir.translation': - import_result = BaseModel.load(self.pool['ir.translation'], cr, uid, import_fields, data, context=context) - else: - import_result = self.pool[record.res_model].load(cr, uid, import_fields, data, context=context) - _logger.info('done') - - # If transaction aborted, RELEASE SAVEPOINT is going to raise - # an InternalError (ROLLBACK should work, maybe). Ignore that. - # TODO: to handle multiple errors, create savepoint around - # write and release it in case of write error (after - # adding error to errors array) => can keep on trying to - # import stuff, and rollback at the end if there is any - # error in the results. - try: - if dryrun: - cr.execute('ROLLBACK TO SAVEPOINT import') - else: - cr.execute('RELEASE SAVEPOINT import') - except psycopg2.InternalError: - pass - - return import_result['messages'] +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import csv +import itertools +import logging +import operator +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +import psycopg2 +from openerp.osv import orm, fields +from openerp.tools.translate import _ +FIELDS_RECURSION_LIMIT = 2 +ERROR_PREVIEW_BYTES = 200 +_logger = logging.getLogger(__name__) + +class ir_import(orm.TransientModel): + _name = 'base_import.import' + _transient_max_hours = 12.0 + _columns = {'res_model': fields.char('Model', size=64), + 'file': fields.binary('File', help='File to check and/or import, raw binary (not base64)'), + 'file_name': fields.char('File Name', size=None), + 'file_type': fields.char('File Type', size=None)} + + def get_fields(self, cr, uid, model, context = None, depth = FIELDS_RECURSION_LIMIT): + """ Recursively get fields for the provided model (through + fields_get) and filter them according to importability + + The output format is a list of ``Field``, with ``Field`` + defined as: + + .. class:: Field + + .. attribute:: id (str) + + A non-unique identifier for the field, used to compute + the span of the ``required`` attribute: if multiple + ``required`` fields have the same id, only one of them + is necessary. + + .. attribute:: name (str) + + The field's logical (OpenERP) name within the scope of + its parent. + + .. attribute:: string (str) + + The field's human-readable name (``@string``) + + .. attribute:: required (bool) + + Whether the field is marked as required in the + model. Clients must provide non-empty import values + for all required fields or the import will error out. + + .. attribute:: fields (list(Field)) + + The current field's subfields. The database and + external identifiers for m2o and m2m fields; a + filtered and transformed fields_get for o2m fields (to + a variable depth defined by ``depth``). + + Fields with no sub-fields will have an empty list of + sub-fields. + + :param str model: name of the model to get fields form + :param int landing: depth of recursion into o2m fields + """ + fields = [{'id': 'id', + 'name': 'id', + 'string': _('External ID'), + 'required': False, + 'fields': []}] + fields_got = self.pool[model].fields_get(cr, uid, context=context) + for name, field in fields_got.iteritems(): + if field.get('deprecated', False) is not False: + continue + if field.get('readonly'): + states = field.get('states') + if not states: + continue + if not any((attr == 'readonly' and value is False for attr, value in itertools.chain.from_iterable(states.itervalues()))): + continue + f = {'id': name, + 'name': name, + 'string': field['string'], + 'required': bool(field.get('required')), + 'fields': []} + if field['type'] in ('many2many', 'many2one'): + f['fields'] = [dict(f, name='id', string=_('External ID')), dict(f, name='.id', string=_('Database ID'))] + elif field['type'] == 'one2many' and depth: + f['fields'] = self.get_fields(cr, uid, field['relation'], context=context, depth=depth - 1) + fields.append(f) + + return fields + + def _read_csv(self, record, options): + """ Returns a CSV-parsed iterator of all empty lines in the file + + :throws csv.Error: if an error is detected during CSV parsing + :throws UnicodeDecodeError: if ``options.encoding`` is incorrect + """ + csv_iterator = csv.reader(StringIO(record.file), quotechar=str(options['quoting']), delimiter=str(options['separator'])) + csv_nonempty = itertools.ifilter(None, csv_iterator) + encoding = options.get('encoding', 'utf-8') + return itertools.imap(lambda row: [ item.decode(encoding) for item in row ], csv_nonempty) + + def _match_header(self, header, fields, options): + """ Attempts to match a given header to a field of the + imported model. + + :param str header: header name from the CSV file + :param fields: + :param dict options: + :returns: an empty list if the header couldn't be matched, or + all the fields to traverse + :rtype: list(Field) + """ + for field in fields: + if header == field['name'] or header.lower() == field['string'].lower(): + return [field] + + if '/' not in header: + return [] + traversal = [] + subfields = fields + for section in header.split('/'): + match = self._match_header(section.strip(), subfields, options) + if not match: + return [] + field = match[0] + subfields = field['fields'] + traversal.append(field) + + return traversal + + def _match_headers(self, rows, fields, options): + """ Attempts to match the imported model's fields to the + titles of the parsed CSV file, if the file is supposed to have + headers. + + Will consume the first line of the ``rows`` iterator. + + Returns a pair of (None, None) if headers were not requested + or the list of headers and a dict mapping cell indices + to key paths in the ``fields`` tree + + :param Iterator rows: + :param dict fields: + :param dict options: + :rtype: (None, None) | (list(str), dict(int: list(str))) + """ + if not options.get('headers'): + return (None, None) + else: + headers = next(rows) + return (headers, dict(((index, [ field['name'] for field in self._match_header(header, fields, options) ] or None) for index, header in enumerate(headers)))) + + def parse_preview(self, cr, uid, id, options, count = 10, context = None): + """ Generates a preview of the uploaded files, and performs + fields-matching between the import's file data and the model's + columns. + + If the headers are not requested (not options.headers), + ``matches`` and ``headers`` are both ``False``. + + :param id: identifier of the import + :param int count: number of preview lines to generate + :param options: format-specific options. + CSV: {encoding, quoting, separator, headers} + :type options: {str, str, str, bool} + :returns: {fields, matches, headers, preview} | {error, preview} + :rtype: {dict(str: dict(...)), dict(int, list(str)), list(str), list(list(str))} | {str, str} + """ + record, = self.browse(cr, uid, [id], context=context) + fields = self.get_fields(cr, uid, record.res_model, context=context) + try: + rows = self._read_csv(record, options) + headers, matches = self._match_headers(rows, fields, options) + preview = itertools.islice(rows, count) + return {'fields': fields, + 'matches': matches or False, + 'headers': headers or False, + 'preview': list(preview)} + except Exception as e: + _logger.debug('Error during CSV parsing preview', exc_info=True) + return {'error': str(e), + 'preview': record.file[:ERROR_PREVIEW_BYTES].decode('iso-8859-1')} + + def _convert_import_data(self, record, fields, options, context = None): + """ Extracts the input browse_record and fields list (with + ``False``-y placeholders for fields to *not* import) into a + format Model.import_data can use: a fields list without holes + and the precisely matching data matrix + + :param browse_record record: + :param list(str|bool): fields + :returns: (data, fields) + :rtype: (list(list(str)), list(str)) + :raises ValueError: in case the import data could not be converted + """ + indices = [ index for index, field in enumerate(fields) if field ] + if not indices: + raise ValueError(_('You must configure at least one field to import')) + if len(indices) == 1: + mapper = lambda row: [row[indices[0]]] + else: + mapper = operator.itemgetter(*indices) + import_fields = filter(None, fields) + rows_to_import = self._read_csv(record, options) + if options.get('headers'): + rows_to_import = itertools.islice(rows_to_import, 1, None) + data = [ row for row in itertools.imap(mapper, rows_to_import) if any(row) ] + return (data, import_fields) + + def do(self, cr, uid, id, fields, options, dryrun = False, context = None): + """ Actual execution of the import + + :param fields: import mapping: maps each column to a field, + ``False`` for the columns to ignore + :type fields: list(str|bool) + :param dict options: + :param bool dryrun: performs all import operations (and + validations) but rollbacks writes, allows + getting as much errors as possible without + the risk of clobbering the database. + :returns: A list of errors. If the list is empty the import + executed fully and correctly. If the list is + non-empty it contains dicts with 3 keys ``type`` the + type of error (``error|warning``); ``message`` the + error message associated with the error (a string) + and ``record`` the data which failed to import (or + ``false`` if that data isn't available or provided) + :rtype: list({type, message, record}) + """ + cr.execute('SAVEPOINT import') + record, = self.browse(cr, uid, [id], context=context) + try: + data, import_fields = self._convert_import_data(record, fields, options, context=context) + except ValueError as e: + return [{'type': 'error', + 'message': unicode(e), + 'record': False}] + + _logger.info('importing %d rows...', len(data)) + import_result = self.pool[record.res_model].load(cr, uid, import_fields, data, context=context) + _logger.info('done') + try: + if dryrun: + cr.execute('ROLLBACK TO SAVEPOINT import') + else: + cr.execute('RELEASE SAVEPOINT import') + except psycopg2.InternalError: + pass + + return import_result['messages'] \ No newline at end of file diff --git a/addons/base_import/static/lib/select2/LICENSE b/addons/base_import/static/lib/select2/LICENSE index 3c98f3df42a46..627fddef413ca 100644 --- a/addons/base_import/static/lib/select2/LICENSE +++ b/addons/base_import/static/lib/select2/LICENSE @@ -1,18 +1,12 @@ Copyright 2012 Igor Vaynberg - + Version: @@ver@@ Timestamp: @@timestamp@@ -This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU -General Public License version 2 (the "GPL License"). You may choose either license to govern your -use of this software only upon the condition that you accept all of the terms of either the Apache -License or the GPL License. - -You may obtain a copy of the Apache License and the GPL License at: +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in +compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: http://www.apache.org/licenses/LICENSE-2.0 -http://www.gnu.org/licenses/gpl-2.0.html -Unless required by applicable law or agreed to in writing, software distributed under the Apache License -or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied. See the Apache License and the GPL License for the specific language governing -permissions and limitations under the Apache License and the GPL License. \ No newline at end of file +Unless required by applicable law or agreed to in writing, software distributed under the License is +distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/addons/base_import/static/lib/select2/README.md b/addons/base_import/static/lib/select2/README.md index cb0be3c6eaafb..3a6dcaf1ba4a2 100644 --- a/addons/base_import/static/lib/select2/README.md +++ b/addons/base_import/static/lib/select2/README.md @@ -1,49 +1,39 @@ Select2 -======= +================= -Select2 is a jQuery-based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. +Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. Look and feel of Select2 is based on the excellent [Chosen](http://harvesthq.github.com/chosen/) library. -To get started, checkout examples and documentation at http://ivaynberg.github.com/select2 +To get started -- checkout http://ivaynberg.github.com/select2! -Use cases ---------- +What Does Select2 Support That Chosen Does Not? +------------------------------------------------- -* Enhancing native selects with search. -* Enhancing native selects with a better multi-select interface. -* Loading data from JavaScript: easily load items via ajax and have them searchable. -* Nesting optgroups: native selects only support one level of nested. Select2 does not have this restriction. -* Tagging: ability to add new items on the fly. -* Working with large, remote datasets: ability to partially load a dataset based on the search term. -* Paging of large datasets: easy support for loading more pages when the results are scrolled to the end. -* Templating: support for custom rendering of results and selections. +* Working with large datasets: Chosen requires the entire dataset to be loaded as `option` tags in the DOM, which limits +it to working with small-ish datasets. Select2 uses a function to find results on-the-fly, which allows it to partially +load results. +* Paging of results: Since Select2 works with large datasets and only loads a small amount of matching results at a time +it has to support paging. Select2 will call the search function when the user scrolls to the bottom of currently loaded +result set allowing for the 'infinite scrolling' of results. +* Custom markup for results: Chosen only supports rendering text results because that is the only markup supported by +`option` tags. Select2 provides an extension point which can be used to produce any kind of markup to represent results. +* Ability to add results on the fly: Select2 provides the ability to add results from the search term entered by the user, which allows it to be used for +tagging. -Browser compatibility ---------------------- -* IE 8+ +Browser Compatibility +-------------------- +* IE 8+ (7 mostly works except for [issue with z-index](https://github.com/ivaynberg/select2/issues/37)) * Chrome 8+ -* Firefox 10+ +* Firefox 3.5+ * Safari 3+ * Opera 10.6+ Integrations ------------ -* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / [Apache Wicket](http://wicket.apache.org)) +* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / Apache Wicket) * [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails) * [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org)) * [Django](https://github.com/applegrew/django-select2) -* [Symfony](https://github.com/19Gerhard85/sfSelect2WidgetsPlugin) -* [Bootstrap](https://github.com/t0m/select2-bootstrap-css) (CSS skin) -* [Yii](https://github.com/tonybolzan/yii-select2) - -Internationalization (i18n) ---------------------------- - -Select2 supports multiple languages by simply including the right -language JS file (`select2_locale_it.js`, `select2_locale_nl.js`, etc.). - -Missing a language? Just copy `select2_locale_en.js.template`, translate -it, and make a pull request back to Select2 here on GitHub. Bug tracker ----------- @@ -52,6 +42,7 @@ Have a bug? Please create an issue here on GitHub! https://github.com/ivaynberg/select2/issues + Mailing list ------------ @@ -62,22 +53,16 @@ select2@googlegroups.com https://groups.google.com/d/forum/select2 -Copyright and license +Copyright and License --------------------- Copyright 2012 Igor Vaynberg -This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU -General Public License version 2 (the "GPL License"). You may choose either license to govern your -use of this software only upon the condition that you accept all of the terms of either the Apache -License or the GPL License. - -You may obtain a copy of the Apache License and the GPL License in the LICENSE file, or at: +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in +compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: http://www.apache.org/licenses/LICENSE-2.0 -http://www.gnu.org/licenses/gpl-2.0.html -Unless required by applicable law or agreed to in writing, software distributed under the Apache License -or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied. See the Apache License and the GPL License for the specific language governing -permissions and limitations under the Apache License and the GPL License. +Unless required by applicable law or agreed to in writing, software distributed under the License is +distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/addons/base_import/static/lib/select2/release.sh b/addons/base_import/static/lib/select2/release.sh old mode 100755 new mode 100644 index 92fe0e959fa3c..79eaabd621d85 --- a/addons/base_import/static/lib/select2/release.sh +++ b/addons/base_import/static/lib/select2/release.sh @@ -15,33 +15,21 @@ js="$name.js" mini="$name.min.js" css="$name.css" release="$name-$ver" -tag="$ver" +releasedir="/tmp/$release" +tag="release-$ver" branch="build-$ver" curbranch=`git branch | grep "*" | sed "s/* //"` timestamp=$(date) tokens="s/@@ver@@/$ver/g;s/\@@timestamp@@/$timestamp/g" remote="github" -echo "Pulling from origin" - -git pull - -echo "Updating Version Identifiers" - -sed -E -e "s/\"version\": \"([0-9\.]+)\",/\"version\": \"$ver\",/g" -i "" bower.json select2.jquery.json -git add bower.json -git add select2.jquery.json -git commit -m "modified version identifiers in descriptors for release $ver" -git push - git branch "$branch" git checkout "$branch" echo "Tokenizing..." -find . -name "$js" | xargs -I{} sed -e "$tokens" -i "" {} -find . -name "$css" | xargs -I{} sed -e "$tokens" -i "" {} -sed -e "s/latest/$ver/g" -i "" bower.json +find . -name "$js" | xargs sed -i -e "$tokens" +find . -name "$css" | xargs sed -i -e "$tokens" git add "$js" git add "$css" @@ -53,8 +41,11 @@ cat LICENSE | sed "$tokens" >> "$mini" echo "*/" >> "$mini" curl -s \ + -d compilation_level=SIMPLE_OPTIMIZATIONS \ + -d output_format=text \ + -d output_info=compiled_code \ --data-urlencode "js_code@$js" \ - http://marijnhaverbeke.nl/uglifyjs \ + http://closure-compiler.appspot.com/compile \ >> "$mini" git add "$mini" @@ -62,12 +53,25 @@ git add "$mini" git commit -m "release $ver" echo "Tagging..." + git tag -a "$tag" -m "tagged version $ver" git push "$remote" --tags +echo "Archiving..." + +rm -rf "$releasedir" +mkdir "$releasedir" + +cp $name.* "$releasedir" +cp spinner.gif "$releasedir" +cp README.* "$releasedir" + +zip -r "$releasedir.zip" "$releasedir" +rm -rf "$releasedir" + echo "Cleaning Up..." git checkout "$curbranch" git branch -D "$branch" -echo "Done" +echo "Done. Release archive created: $releasedir.zip" diff --git a/addons/base_import/static/lib/select2/select2.css b/addons/base_import/static/lib/select2/select2.css index 2b32ed6f28005..f0bdfb82f0486 100644 --- a/addons/base_import/static/lib/select2/select2.css +++ b/addons/base_import/static/lib/select2/select2.css @@ -1,317 +1,279 @@ /* -Version: 3.4.5 Timestamp: Mon Nov 4 08:22:42 PST 2013 +Version: @@ver@@ Timestamp: @@timestamp@@ */ .select2-container { - margin: 0; position: relative; display: inline-block; /* inline-block for ie7 */ zoom: 1; *display: inline; - vertical-align: middle; + vertical-align: top; } .select2-container, .select2-drop, .select2-search, -.select2-search input { +.select2-search input{ /* Force border-box so that % widths fit the parent container without overlap because of margin/padding. More Info : http://www.quirksmode.org/css/box.html */ + -moz-box-sizing: border-box; /* firefox */ + -ms-box-sizing: border-box; /* ie */ -webkit-box-sizing: border-box; /* webkit */ - -moz-box-sizing: border-box; /* firefox */ - box-sizing: border-box; /* css3 */ + -khtml-box-sizing: border-box; /* konqueror */ + box-sizing: border-box; /* css3 */ } .select2-container .select2-choice { + background-color: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%); + background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0); + background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #aaa; display: block; - height: 26px; - padding: 0 0 0 8px; overflow: hidden; - position: relative; - - border: 1px solid #aaa; white-space: nowrap; + position: relative; + height: 26px; line-height: 26px; + padding: 0 0 0 8px; color: #444; text-decoration: none; - - border-radius: 4px; - - background-clip: padding-box; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - - background-color: #fff; - background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff)); - background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%); - background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0); - background-image: linear-gradient(top, #fff 0%, #eee 50%); } -.select2-container.select2-drop-above .select2-choice { +.select2-container.select2-drop-above .select2-choice +{ border-bottom-color: #aaa; - - border-radius: 0 0 4px 4px; - - background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff)); - background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%); - background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0); - background-image: linear-gradient(top, #eee 0%, #fff 90%); -} - -.select2-container.select2-allowclear .select2-choice .select2-chosen { - margin-right: 42px; -} - -.select2-container .select2-choice > .select2-chosen { + -webkit-border-radius:0px 0px 4px 4px; + -moz-border-radius:0px 0px 4px 4px; + border-radius:0px 0px 4px 4px; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%); + background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); + background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%); +} + +.select2-container .select2-choice span { margin-right: 26px; display: block; overflow: hidden; - white-space: nowrap; - + -o-text-overflow: ellipsis; + -ms-text-overflow: ellipsis; text-overflow: ellipsis; } .select2-container .select2-choice abbr { - display: none; - width: 12px; - height: 12px; - position: absolute; - right: 24px; - top: 8px; - - font-size: 1px; - text-decoration: none; - - border: 0; - background: url('select2.png') right top no-repeat; - cursor: pointer; - outline: 0; -} - -.select2-container.select2-allowclear .select2-choice abbr { - display: inline-block; + display: block; + position: absolute; + right: 26px; + top: 8px; + width: 12px; + height: 12px; + font-size: 1px; + background: url('select2.png') right top no-repeat; + cursor: pointer; + text-decoration: none; + border:0; + outline: 0; } - .select2-container .select2-choice abbr:hover { - background-position: right -11px; - cursor: pointer; -} - -.select2-drop-mask { - border: 0; - margin: 0; - padding: 0; - position: fixed; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 9998; - /* styles required for IE to work */ - background-color: #fff; - filter: alpha(opacity=0); + background-position: right -11px; + cursor: pointer; } .select2-drop { - width: 100%; - margin-top: -1px; - position: absolute; - z-index: 9999; - top: 100%; - background: #fff; color: #000; border: 1px solid #aaa; border-top: 0; - - border-radius: 0 0 4px 4px; - + position: absolute; + top: 100%; -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); - box-shadow: 0 4px 5px rgba(0, 0, 0, .15); -} - -.select2-drop-auto-width { - border-top: 1px solid #aaa; - width: auto; -} + -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + -o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + z-index: 9999; + width:100%; + margin-top:-1px; -.select2-drop-auto-width .select2-search { - padding-top: 4px; + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; } .select2-drop.select2-drop-above { - margin-top: 1px; + -webkit-border-radius: 4px 4px 0px 0px; + -moz-border-radius: 4px 4px 0px 0px; + border-radius: 4px 4px 0px 0px; + margin-top:1px; border-top: 1px solid #aaa; border-bottom: 0; - border-radius: 4px 4px 0 0; - -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); - box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); -} - -.select2-drop-active { - border: 1px solid #5897fb; - border-top: none; -} - -.select2-drop.select2-drop-above.select2-drop-active { - border-top: 1px solid #5897fb; + -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + -o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); } -.select2-container .select2-choice .select2-arrow { - display: inline-block; - width: 18px; - height: 100%; - position: absolute; - right: 0; - top: 0; - - border-left: 1px solid #aaa; +.select2-container .select2-choice div { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; - + -moz-background-clip: padding; + -webkit-background-clip: padding-box; background-clip: padding-box; - background: #ccc; background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%); background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0); - background-image: linear-gradient(top, #ccc 0%, #eee 60%); + background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%); + background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0); + background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%); + border-left: 1px solid #aaa; + position: absolute; + right: 0; + top: 0; + display: block; + height: 100%; + width: 18px; } -.select2-container .select2-choice .select2-arrow b { +.select2-container .select2-choice div b { + background: url('select2.png') no-repeat 0 1px; display: block; width: 100%; height: 100%; - background: url('select2.png') no-repeat 0 1px; } .select2-search { - display: inline-block; - width: 100%; - min-height: 26px; - margin: 0; - padding-left: 4px; - padding-right: 4px; - - position: relative; + display: inline-block; + white-space: nowrap; z-index: 10000; + min-height: 26px; + width: 100%; + margin: 0; + padding-left: 4px; + padding-right: 4px; +} - white-space: nowrap; +.select2-search-hidden { + display: block; + position: absolute; + left: -10000px; } .select2-search input { - width: 100%; - height: auto !important; - min-height: 26px; + background: #fff url('select2.png') no-repeat 100% -22px; + background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%); padding: 4px 20px 4px 5px; - margin: 0; - outline: 0; + border: 1px solid #aaa; font-family: sans-serif; font-size: 1em; - - border: 1px solid #aaa; - border-radius: 0; - + width:100%; + margin:0; + height:auto !important; + min-height: 26px; -webkit-box-shadow: none; - box-shadow: none; - - background: #fff url('select2.png') no-repeat 100% -22px; - background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee)); - background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%); - background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%); - background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #fff 85%, #eee 99%); + -moz-box-shadow: none; + box-shadow: none; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; } -.select2-drop.select2-drop-above .select2-search input { - margin-top: 4px; +.select2-drop.select2-drop-above .select2-search input +{ + margin-top:4px; } .select2-search input.select2-active { - background: #fff url('select2-spinner.gif') no-repeat 100%; - background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee)); - background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%); - background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%); - background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #fff 85%, #eee 99%); + background: #fff url('spinner.gif') no-repeat 100%; + background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%); } + .select2-container-active .select2-choice, .select2-container-active .select2-choices { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); border: 1px solid #5897fb; outline: none; - - -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3); - box-shadow: 0 0 5px rgba(0, 0, 0, .3); } .select2-dropdown-open .select2-choice { - border-bottom-color: transparent; - -webkit-box-shadow: 0 1px 0 #fff inset; - box-shadow: 0 1px 0 #fff inset; - - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - - background-color: #eee; - background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee)); - background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%); - background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0); - background-image: linear-gradient(top, #fff 0%, #eee 50%); -} - -.select2-dropdown-open.select2-drop-above .select2-choice, -.select2-dropdown-open.select2-drop-above .select2-choices { - border: 1px solid #5897fb; - border-top-color: transparent; - - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee)); - background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%); - background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0); - background-image: linear-gradient(bottom, #fff 0%, #eee 50%); -} - -.select2-dropdown-open .select2-choice .select2-arrow { - background: transparent; - border-left: none; - filter: none; -} -.select2-dropdown-open .select2-choice .select2-arrow b { - background-position: -18px 1px; + border: 1px solid #aaa; + border-bottom-color: transparent; + -webkit-box-shadow: 0 1px 0 #fff inset; + -moz-box-shadow : 0 1px 0 #fff inset; + -o-box-shadow : 0 1px 0 #fff inset; + box-shadow : 0 1px 0 #fff inset; + background-color: #eee; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); + background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); + background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%); + -webkit-border-bottom-left-radius : 0; + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomleft : 0; + -moz-border-radius-bottomright: 0; + border-bottom-left-radius : 0; + border-bottom-right-radius: 0; +} + +.select2-dropdown-open .select2-choice div { + background: transparent; + border-left: none; +} +.select2-dropdown-open .select2-choice div b { + background-position: -18px 1px; } /* results */ .select2-results { - max-height: 200px; - padding: 0 0 0 4px; - margin: 4px 4px 4px 0; - position: relative; - overflow-x: hidden; - overflow-y: auto; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + margin: 4px 4px 4px 0; + padding: 0 0 0 4px; + position: relative; + overflow-x: hidden; + overflow-y: auto; + max-height: 200px; } .select2-results ul.select2-result-sub { - margin: 0; - padding-left: 0; + margin: 0 0 0 0; } .select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px } @@ -323,59 +285,40 @@ Version: 3.4.5 Timestamp: Mon Nov 4 08:22:42 PST 2013 .select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px } .select2-results li { - list-style: none; - display: list-item; - background-image: none; + list-style: none; + display: list-item; } .select2-results li.select2-result-with-children > .select2-result-label { - font-weight: bold; + font-weight: bold; } .select2-results .select2-result-label { - padding: 3px 7px 4px; - margin: 0; - cursor: pointer; - - min-height: 1em; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + padding: 3px 7px 4px; + margin: 0; + cursor: pointer; } .select2-results .select2-highlighted { - background: #3875d7; - color: #fff; + background: #3875d7; + color: #fff; } - .select2-results li em { - background: #feffde; - font-style: normal; + background: #feffde; + font-style: normal; } - .select2-results .select2-highlighted em { - background: transparent; -} - -.select2-results .select2-highlighted ul { - background: #fff; - color: #000; + background: transparent; } - - .select2-results .select2-no-results, .select2-results .select2-searching, .select2-results .select2-selection-limit { - background: #f4f4f4; - display: list-item; + background: #f4f4f4; + display: list-item; } /* -disabled look for disabled choices in the results dropdown -*/ +disabled look for already selected choices in the results dropdown .select2-results .select2-disabled.select2-highlighted { color: #666; background: #f4f4f4; @@ -387,18 +330,18 @@ disabled look for disabled choices in the results dropdown display: list-item; cursor: default; } - -.select2-results .select2-selected { +*/ +.select2-results .select2-disabled { display: none; } .select2-more-results.select2-active { - background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%; + background: #f4f4f4 url('spinner.gif') no-repeat 100%; } .select2-more-results { - background: #f4f4f4; - display: list-item; + background: #f4f4f4; + display: list-item; } /* disabled styles */ @@ -410,39 +353,31 @@ disabled look for disabled choices in the results dropdown cursor: default; } -.select2-container.select2-container-disabled .select2-choice .select2-arrow { +.select2-container.select2-container-disabled .select2-choice div { background-color: #f4f4f4; background-image: none; border-left: 0; } -.select2-container.select2-container-disabled .select2-choice abbr { - display: none; -} - /* multiselect */ .select2-container-multi .select2-choices { - height: auto !important; - height: 1%; - margin: 0; - padding: 0; - position: relative; - - border: 1px solid #aaa; - cursor: text; - overflow: hidden; - background-color: #fff; - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff)); - background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%); - background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%); - background-image: linear-gradient(top, #eee 1%, #fff 15%); -} - -.select2-locked { - padding: 3px 5px 3px 5px !important; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%); + border: 1px solid #aaa; + margin: 0; + padding: 0; + cursor: text; + overflow: hidden; + height: auto !important; + height: 1%; + position: relative; } .select2-container-multi .select2-choices { @@ -450,107 +385,107 @@ disabled look for disabled choices in the results dropdown } .select2-container-multi.select2-container-active .select2-choices { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); border: 1px solid #5897fb; outline: none; - - -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3); - box-shadow: 0 0 5px rgba(0, 0, 0, .3); } .select2-container-multi .select2-choices li { - float: left; - list-style: none; + float: left; + list-style: none; } .select2-container-multi .select2-choices .select2-search-field { - margin: 0; - padding: 0; - white-space: nowrap; + white-space: nowrap; + margin: 0; + padding: 0; } .select2-container-multi .select2-choices .select2-search-field input { - padding: 5px; - margin: 1px 0; - - font-family: sans-serif; - font-size: 100%; - color: #666; - outline: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - background: transparent !important; + color: #666; + background: transparent !important; + font-family: sans-serif; + font-size: 100%; + height: 15px; + padding: 5px; + margin: 1px 0; + outline: 0; + border: 0; + -webkit-box-shadow: none; + -moz-box-shadow : none; + -o-box-shadow : none; + box-shadow : none; } .select2-container-multi .select2-choices .select2-search-field input.select2-active { - background: #fff url('select2-spinner.gif') no-repeat 100% !important; + background: #fff url('spinner.gif') no-repeat 100% !important; } .select2-default { - color: #999 !important; + color: #999 !important; } .select2-container-multi .select2-choices .select2-search-choice { - padding: 3px 5px 3px 18px; - margin: 3px 0 3px 5px; - position: relative; - - line-height: 13px; - color: #333; - cursor: default; - border: 1px solid #aaaaaa; - - border-radius: 3px; - - -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05); - box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05); - - background-clip: padding-box; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - - background-color: #e4e4e4; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0); - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee)); - background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); - background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); - background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); + -webkit-border-radius: 3px; + -moz-border-radius : 3px; + border-radius : 3px; + -moz-background-clip : padding; + -webkit-background-clip: padding-box; + background-clip : padding-box; + background-color: #e4e4e4; + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + color: #333; + border: 1px solid #aaaaaa; + line-height: 13px; + padding: 3px 5px 3px 18px; + margin: 3px 0 3px 5px; + position: relative; + cursor: default; } -.select2-container-multi .select2-choices .select2-search-choice .select2-chosen { - cursor: default; +.select2-container-multi .select2-choices .select2-search-choice span { + cursor: default; } .select2-container-multi .select2-choices .select2-search-choice-focus { - background: #d4d4d4; + background: #d4d4d4; } .select2-search-choice-close { - display: block; - width: 12px; - height: 13px; - position: absolute; - right: 3px; - top: 4px; - - font-size: 1px; - outline: none; - background: url('select2.png') right top no-repeat; + display: block; + position: absolute; + right: 3px; + top: 4px; + width: 12px; + height: 13px; + font-size: 1px; + background: url('select2.png') right top no-repeat; + outline: none; } .select2-container-multi .select2-search-choice-close { - left: 3px; + left: 3px; } + .select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover { background-position: right -11px; } .select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close { - background-position: right -11px; + background-position: right -11px; } /* disabled styles */ -.select2-container-multi.select2-container-disabled .select2-choices { + +.select2-container-multi.select2-container-disabled .select2-choices{ background-color: #f4f4f4; background-image: none; border: 1px solid #ddd; @@ -558,58 +493,32 @@ disabled look for disabled choices in the results dropdown } .select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { - padding: 3px 5px 3px 5px; - border: 1px solid #ddd; background-image: none; background-color: #f4f4f4; + border: 1px solid #ddd; + padding: 3px 5px 3px 5px; } -.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none; - background: none; +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { + display: none; } /* end multiselect */ - .select2-result-selectable .select2-match, -.select2-result-unselectable .select2-match { - text-decoration: underline; -} +.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; } +.select2-result-unselectable .select2-match { text-decoration: none; } -.select2-offscreen, .select2-offscreen:focus { - clip: rect(0 0 0 0) !important; - width: 1px !important; - height: 1px !important; - border: 0 !important; - margin: 0 !important; - padding: 0 !important; - overflow: hidden !important; - position: absolute !important; - outline: 0 !important; - left: 0px !important; - top: 0px !important; -} +.select2-offscreen { position: absolute; left: -10000px; } -.select2-display-none { - display: none; -} - -.select2-measure-scrollbar { - position: absolute; - top: -10000px; - left: -10000px; - width: 100px; - height: 100px; - overflow: scroll; -} /* Retina-ize icons */ -@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) { - .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice .select2-arrow b { - background-image: url('select2x2.png') !important; - background-repeat: no-repeat !important; - background-size: 60px 40px !important; - } - .select2-search input { - background-position: 100% -21px !important; - } -} +@media only screen and (-webkit-min-device-pixel-ratio: 1.5) { + .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b { + background-image: url(select2x2.png) !important; + background-repeat: no-repeat !important; + background-size: 60px 40px !important; + } + .select2-search input { + background-position: 100% -21px !important; + } +} \ No newline at end of file diff --git a/addons/base_import/static/lib/select2/select2.js b/addons/base_import/static/lib/select2/select2.js index 3b5e8e280cc4d..42ce0a928b8bb 100644 --- a/addons/base_import/static/lib/select2/select2.js +++ b/addons/base_import/static/lib/select2/select2.js @@ -1,41 +1,35 @@ /* -Copyright 2012 Igor Vaynberg - -Version: 3.4.5 Timestamp: Mon Nov 4 08:22:42 PST 2013 - -This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU -General Public License version 2 (the "GPL License"). You may choose either license to govern your -use of this software only upon the condition that you accept all of the terms of either the Apache -License or the GPL License. - -You may obtain a copy of the Apache License and the GPL License at: - - http://www.apache.org/licenses/LICENSE-2.0 - http://www.gnu.org/licenses/gpl-2.0.html - -Unless required by applicable law or agreed to in writing, software distributed under the -Apache License or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for -the specific language governing permissions and limitations under the Apache License and the GPL License. -*/ -(function ($) { - if(typeof $.fn.each2 == "undefined") { - $.extend($.fn, { - /* - * 4-10 times faster .each replacement - * use it carefully, as it overrides jQuery context of element on each iteration - */ - each2 : function (c) { - var j = $([0]), i = -1, l = this.length; - while ( - ++i < l - && (j.context = j[0] = this[i]) - && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object - ); - return this; - } - }); - } + Copyright 2012 Igor Vaynberg + + Version: @@ver@@ Timestamp: @@timestamp@@ + + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in + compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed under the License is + distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. + */ + (function ($) { + if(typeof $.fn.each2 == "undefined"){ + $.fn.extend({ + /* + * 4-10 times faster .each replacement + * use it carefully, as it overrides jQuery context of element on each iteration + */ + each2 : function (c) { + var j = $([0]), i = -1, l = this.length; + while ( + ++i < l + && (j.context = j[0] = this[i]) + && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object + ); + return this; + } + }); + } })(jQuery); (function ($, undefined) { @@ -46,8 +40,7 @@ the specific language governing permissions and limitations under the Apache Lic return; } - var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer, - lastMousePosition={x:0,y:0}, $document, scrollBarDimensions, + var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer; KEY = { TAB: 9, @@ -95,52 +88,34 @@ the specific language governing permissions and limitations under the Apache Lic k = k.which ? k.which : k; return k >= 112 && k <= 123; } - }, - MEASURE_SCROLLBAR_TEMPLATE = "
    ", - - DIACRITICS = {"\u24B6":"A","\uFF21":"A","\u00C0":"A","\u00C1":"A","\u00C2":"A","\u1EA6":"A","\u1EA4":"A","\u1EAA":"A","\u1EA8":"A","\u00C3":"A","\u0100":"A","\u0102":"A","\u1EB0":"A","\u1EAE":"A","\u1EB4":"A","\u1EB2":"A","\u0226":"A","\u01E0":"A","\u00C4":"A","\u01DE":"A","\u1EA2":"A","\u00C5":"A","\u01FA":"A","\u01CD":"A","\u0200":"A","\u0202":"A","\u1EA0":"A","\u1EAC":"A","\u1EB6":"A","\u1E00":"A","\u0104":"A","\u023A":"A","\u2C6F":"A","\uA732":"AA","\u00C6":"AE","\u01FC":"AE","\u01E2":"AE","\uA734":"AO","\uA736":"AU","\uA738":"AV","\uA73A":"AV","\uA73C":"AY","\u24B7":"B","\uFF22":"B","\u1E02":"B","\u1E04":"B","\u1E06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24B8":"C","\uFF23":"C","\u0106":"C","\u0108":"C","\u010A":"C","\u010C":"C","\u00C7":"C","\u1E08":"C","\u0187":"C","\u023B":"C","\uA73E":"C","\u24B9":"D","\uFF24":"D","\u1E0A":"D","\u010E":"D","\u1E0C":"D","\u1E10":"D","\u1E12":"D","\u1E0E":"D","\u0110":"D","\u018B":"D","\u018A":"D","\u0189":"D","\uA779":"D","\u01F1":"DZ","\u01C4":"DZ","\u01F2":"Dz","\u01C5":"Dz","\u24BA":"E","\uFF25":"E","\u00C8":"E","\u00C9":"E","\u00CA":"E","\u1EC0":"E","\u1EBE":"E","\u1EC4":"E","\u1EC2":"E","\u1EBC":"E","\u0112":"E","\u1E14":"E","\u1E16":"E","\u0114":"E","\u0116":"E","\u00CB":"E","\u1EBA":"E","\u011A":"E","\u0204":"E","\u0206":"E","\u1EB8":"E","\u1EC6":"E","\u0228":"E","\u1E1C":"E","\u0118":"E","\u1E18":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z"}; - - $document = $(document); + }; nextUid=(function() { var counter=1; return function() { return counter++; }; }()); + function indexOf(value, array) { + var i = 0, l = array.length, v; - function stripDiacritics(str) { - var ret, i, l, c; - - if (!str || str.length < 1) return str; - - ret = ""; - for (i = 0, l = str.length; i < l; i++) { - c = str.charAt(i); - ret += DIACRITICS[c] || c; + if (typeof value === "undefined") { + return -1; } - return ret; - } - function indexOf(value, array) { - var i = 0, l = array.length; - for (; i < l; i = i + 1) { - if (equal(value, array[i])) return i; + if (value.constructor === String) { + for (; i < l; i = i + 1) if (value.localeCompare(array[i]) === 0) return i; + } else { + for (; i < l; i = i + 1) { + v = array[i]; + if (v.constructor === String) { + if (v.localeCompare(value) === 0) return i; + } else { + if (v === value) return i; + } + } } return -1; } - function measureScrollbar () { - var $template = $( MEASURE_SCROLLBAR_TEMPLATE ); - $template.appendTo('body'); - - var dim = { - width: $template.width() - $template[0].clientWidth, - height: $template.height() - $template[0].clientHeight - }; - $template.remove(); - - return dim; - } - /** - * Compares equality of a and b + * Compares equality of a and b taking into account that a and b may be strings, in which case localeCompare is used * @param a * @param b */ @@ -148,10 +123,8 @@ the specific language governing permissions and limitations under the Apache Lic if (a === b) return true; if (a === undefined || b === undefined) return false; if (a === null || b === null) return false; - // Check whether 'a' or 'b' is a string (primitive or object). - // The concatenation of an empty string (+'') converts its argument to a string's primitive. - if (a.constructor === String) return a+'' === b+''; // a+'' - in case 'a' is a String object - if (b.constructor === String) return b+'' === a+''; // b+'' - in case 'b' is a String object + if (a.constructor === String) return a.localeCompare(b) === 0; + if (b.constructor === String) return b.localeCompare(a) === 0; return false; } @@ -170,17 +143,17 @@ the specific language governing permissions and limitations under the Apache Lic } function getSideBorderPadding(element) { - return element.outerWidth(false) - element.width(); + return element.outerWidth() - element.width(); } function installKeyUpChangeEvent(element) { var key="keyup-change-value"; - element.on("keydown", function () { + element.bind("keydown", function () { if ($.data(element, key) === undefined) { $.data(element, key, element.val()); } }); - element.on("keyup", function () { + element.bind("keyup", function () { var val= $.data(element, key); if (val !== undefined && element.val() !== val) { $.removeData(element, key); @@ -189,9 +162,8 @@ the specific language governing permissions and limitations under the Apache Lic }); } - $document.on("mousemove", function (e) { - lastMousePosition.x = e.pageX; - lastMousePosition.y = e.pageY; + $(document).delegate("*", "mousemove", function (e) { + $.data(document, "select2-lastpos", {x: e.pageX, y: e.pageY}); }); /** @@ -201,8 +173,8 @@ the specific language governing permissions and limitations under the Apache Lic * the elements under the pointer are scrolled. */ function installFilteredMouseMove(element) { - element.on("mousemove", function (e) { - var lastpos = lastMousePosition; + element.bind("mousemove", function (e) { + var lastpos = $.data(document, "select2-lastpos"); if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) { $(e.target).trigger("mousemove-filtered", e); } @@ -246,159 +218,63 @@ the specific language governing permissions and limitations under the Apache Lic function installDebouncedScroll(threshold, element) { var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);}); - element.on("scroll", function (e) { + element.bind("scroll", function (e) { if (indexOf(e.target, element.get()) >= 0) notify(e); }); } - function focus($el) { - if ($el[0] === document.activeElement) return; - - /* set the focus in a 0 timeout - that way the focus is set after the processing - of the current event has finished - which seems like the only reliable way - to set focus */ - window.setTimeout(function() { - var el=$el[0], pos=$el.val().length, range; - - $el.focus(); - - /* make sure el received focus so we do not error out when trying to manipulate the caret. - sometimes modals or others listeners may steal it after its set */ - if ($el.is(":visible") && el === document.activeElement) { - - /* after the focus is set move the caret to the end, necessary when we val() - just before setting focus */ - if(el.setSelectionRange) - { - el.setSelectionRange(pos, pos); - } - else if (el.createTextRange) { - range = el.createTextRange(); - range.collapse(false); - range.select(); - } - } - }, 0); - } - - function getCursorInfo(el) { - el = $(el)[0]; - var offset = 0; - var length = 0; - if ('selectionStart' in el) { - offset = el.selectionStart; - length = el.selectionEnd - offset; - } else if ('selection' in document) { - el.focus(); - var sel = document.selection.createRange(); - length = document.selection.createRange().text.length; - sel.moveStart('character', -el.value.length); - offset = sel.text.length - length; - } - return { offset: offset, length: length }; - } - function killEvent(event) { event.preventDefault(); event.stopPropagation(); } - function killEventImmediately(event) { - event.preventDefault(); - event.stopImmediatePropagation(); - } function measureTextWidth(e) { if (!sizer){ - var style = e[0].currentStyle || window.getComputedStyle(e[0], null); - sizer = $(document.createElement("div")).css({ - position: "absolute", - left: "-10000px", - top: "-10000px", - display: "none", - fontSize: style.fontSize, - fontFamily: style.fontFamily, - fontStyle: style.fontStyle, - fontWeight: style.fontWeight, - letterSpacing: style.letterSpacing, - textTransform: style.textTransform, - whiteSpace: "nowrap" - }); - sizer.attr("class","select2-sizer"); - $("body").append(sizer); + var style = e[0].currentStyle || window.getComputedStyle(e[0], null); + sizer = $("
    ").css({ + position: "absolute", + left: "-10000px", + top: "-10000px", + display: "none", + fontSize: style.fontSize, + fontFamily: style.fontFamily, + fontStyle: style.fontStyle, + fontWeight: style.fontWeight, + letterSpacing: style.letterSpacing, + textTransform: style.textTransform, + whiteSpace: "nowrap" + }); + $("body").append(sizer); } sizer.text(e.val()); return sizer.width(); } - function syncCssClasses(dest, src, adapter) { - var classes, replacements = [], adapted; - - classes = dest.attr("class"); - if (classes) { - classes = '' + classes; // for IE which returns object - $(classes.split(" ")).each2(function() { - if (this.indexOf("select2-") === 0) { - replacements.push(this); - } - }); - } - classes = src.attr("class"); - if (classes) { - classes = '' + classes; // for IE which returns object - $(classes.split(" ")).each2(function() { - if (this.indexOf("select2-") !== 0) { - adapted = adapter(this); - if (adapted) { - replacements.push(adapted); - } - } - }); - } - dest.attr("class", replacements.join(" ")); - } - - - function markMatch(text, term, markup, escapeMarkup) { - var match=stripDiacritics(text.toUpperCase()).indexOf(stripDiacritics(term.toUpperCase())), + function markMatch(text, term, markup) { + var match=text.toUpperCase().indexOf(term.toUpperCase()), tl=term.length; if (match<0) { - markup.push(escapeMarkup(text)); + markup.push(text); return; } - markup.push(escapeMarkup(text.substring(0, match))); + markup.push(text.substring(0, match)); markup.push(""); - markup.push(escapeMarkup(text.substring(match, match + tl))); + markup.push(text.substring(match, match + tl)); markup.push(""); - markup.push(escapeMarkup(text.substring(match + tl, text.length))); - } - - function defaultEscapeMarkup(markup) { - var replace_map = { - '\\': '\', - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - "/": '/' - }; - - return String(markup).replace(/[&<>"'\/\\]/g, function (match) { - return replace_map[match]; - }); + markup.push(text.substring(match + tl, text.length)); } /** * Produces an ajax-based query function * * @param options object containing configuration paramters - * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax * @param options.url url for the data * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url. * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified + * @param options.traditional a boolean flag that should be true if you wish to use the traditional style of param serialization for the ajax request * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2. * The expected format is an object containing the following keys: @@ -408,50 +284,39 @@ the specific language governing permissions and limitations under the Apache Lic */ function ajax(options) { var timeout, // current scheduled but not yet executed request + requestSequence = 0, // sequence used to drop out-of-order responses handler = null, - quietMillis = options.quietMillis || 100, - ajaxUrl = options.url, - self = this; + quietMillis = options.quietMillis || 100; return function (query) { window.clearTimeout(timeout); timeout = window.setTimeout(function () { - var data = options.data, // ajax data function - url = ajaxUrl, // ajax url string or function - transport = options.transport || $.fn.select2.ajaxDefaults.transport, - // deprecated - to be removed in 4.0 - use params instead - deprecated = { - type: options.type || 'GET', // set type of request (GET or POST) - cache: options.cache || false, - jsonpCallback: options.jsonpCallback||undefined, - dataType: options.dataType||"json" - }, - params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated); - - data = data ? data.call(self, query.term, query.page, query.context) : null; - url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url; - - if (handler) { handler.abort(); } - - if (options.params) { - if ($.isFunction(options.params)) { - $.extend(params, options.params.call(self)); - } else { - $.extend(params, options.params); - } - } + requestSequence += 1; // increment the sequence + var requestNumber = requestSequence, // this request's sequence number + data = options.data, // ajax data function + transport = options.transport || $.ajax, + traditional = options.traditional || false, + type = options.type || 'GET'; // set type of request (GET or POST) + + data = data.call(this, query.term, query.page, query.context); - $.extend(params, { - url: url, + if( null !== handler) { handler.abort(); } + + handler = transport.call(null, { + url: options.url, dataType: options.dataType, data: data, + type: type, + traditional: traditional, success: function (data) { - // TODO - replace query.page with query so users have access to term, page, etc. + if (requestNumber < requestSequence) { + return; + } + // TODO 3.0 - replace query.page with query so users have access to term, page, etc. var results = options.results(data, query.page); query.callback(results); } }); - handler = transport.call(self, params); }, quietMillis); }; } @@ -473,33 +338,22 @@ the specific language governing permissions and limitations under the Apache Lic function local(options) { var data = options, // data elements dataText, - tmp, text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search - if ($.isArray(data)) { - tmp = data; - data = { results: tmp }; - } - - if ($.isFunction(data) === false) { - tmp = data; - data = function() { return tmp; }; - } - - var dataItem = data(); - if (dataItem.text) { - text = dataItem.text; + if (!$.isArray(data)) { + text = data.text; // if text is not a function we assume it to be a key name if (!$.isFunction(text)) { - dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available - text = function (item) { return item[dataText]; }; + dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available + text = function (item) { return item[dataText]; }; } + data = data.results; } return function (query) { var t = query.term, filtered = { results: [] }, process; if (t === "") { - query.callback(data()); + query.callback({results: data}); return; } @@ -513,27 +367,34 @@ the specific language governing permissions and limitations under the Apache Lic } group.children=[]; $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); }); - if (group.children.length || query.matcher(t, text(group), datum)) { + if (group.children.length) { collection.push(group); } } else { - if (query.matcher(t, text(datum), datum)) { + if (query.matcher(t, text(datum))) { collection.push(datum); } } }; - $(data().results).each2(function(i, datum) { process(datum, filtered.results); }); + $(data).each2(function(i, datum) { process(datum, filtered.results); }); query.callback(filtered); }; } // TODO javadoc function tags(data) { - var isFunc = $.isFunction(data); + // TODO even for a function we should probably return a wrapper that does the same object/string check as + // the function for arrays. otherwise only functions that return objects are supported. + if ($.isFunction(data)) { + return data; + } + + // if not a function we assume it to be an array + return function (query) { var t = query.term, filtered = {results: []}; - $(isFunc ? data() : data).each(function () { + $(data).each(function () { var isObject = this.text !== undefined, text = isObject ? this.text : this; if (t === "" || query.matcher(t, text)) { @@ -555,7 +416,7 @@ the specific language governing permissions and limitations under the Apache Lic function checkFormatter(formatter, formatterName) { if ($.isFunction(formatter)) return true; if (!formatter) return false; - throw new Error(formatterName +" must be a function or a falsy value"); + throw new Error("formatterName must be a function or a falsy value"); } function evaluate(val) { @@ -610,7 +471,7 @@ the specific language governing permissions and limitations under the Apache Lic input = input.substring(index + separator.length); if (token.length > 0) { - token = opts.createSearchChoice.call(this, token, selection); + token = opts.createSearchChoice(token, selection); if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) { dupe = false; for (i = 0, l = selection.length; i < l; i++) { @@ -624,9 +485,38 @@ the specific language governing permissions and limitations under the Apache Lic } } - if (original!==input) return input; + if (original.localeCompare(input) != 0) return input; } + /** + * blurs any Select2 container that has focus when an element outside them was clicked or received focus + * + * also takes care of clicks on label tags that point to the source element + */ + $(document).ready(function () { + $(document).delegate("*", "mousedown touchend", function (e) { + var target = $(e.target).closest("div.select2-container").get(0), attr; + if (target) { + $(document).find("div.select2-container-active").each(function () { + if (this !== target) $(this).data("select2").blur(); + }); + } else { + target = $(e.target).closest("div.select2-drop").get(0); + $(document).find("div.select2-drop-active").each(function () { + if (this !== target) $(this).data("select2").blur(); + }); + } + + target=$(e.target); + attr = target.attr("for"); + if ("LABEL" === e.target.tagName && attr && attr.length > 0) { + target = $("#"+attr); + target = target.data("select2"); + if (target !== undefined) { target.focus(); e.preventDefault();} + } + }); + }); + /** * Creates a new class * @@ -664,9 +554,10 @@ the specific language governing permissions and limitations under the Apache Lic // destroy if called on an existing component if (opts.element.data("select2") !== undefined && opts.element.data("select2") !== null) { - opts.element.data("select2").destroy(); + this.destroy(); } + this.enabled=true; this.container = this.createContainer(); this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid()); @@ -676,57 +567,46 @@ the specific language governing permissions and limitations under the Apache Lic // cache the body so future lookups are cheap this.body = thunk(function() { return opts.element.closest("body"); }); - syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); + if (opts.element.attr("class") !== undefined) { + this.container.addClass(opts.element.attr("class").replace(/validate\[[\S ]+] ?/, '')); + } - this.container.attr("style", opts.element.attr("style")); this.container.css(evaluate(opts.containerCss)); this.container.addClass(evaluate(opts.containerCssClass)); - this.elementTabIndex = this.opts.element.attr("tabindex"); - // swap container for the element this.opts.element .data("select2", this) - .attr("tabindex", "-1") - .before(this.container) - .on("click.select2", killEvent); // do not leak click events - + .hide() + .before(this.container); this.container.data("select2", this); this.dropdown = this.container.find(".select2-drop"); - - syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass); - this.dropdown.addClass(evaluate(opts.dropdownCssClass)); this.dropdown.data("select2", this); - this.dropdown.on("click", killEvent); this.results = results = this.container.find(resultsSelector); this.search = search = this.container.find("input.select2-input"); - this.queryCount = 0; + search.attr("tabIndex", this.opts.element.attr("tabIndex")); + this.resultsPage = 0; this.context = null; // initialize the container this.initContainer(); - - this.container.on("click", killEvent); + this.initContainerWidth(); installFilteredMouseMove(this.results); - this.dropdown.on("mousemove-filtered touchstart touchmove touchend", resultsSelector, this.bind(this.highlightUnderEvent)); + this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent)); installDebouncedScroll(80, this.results); - this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded)); - - // do not propagate change event from the search field out of the component - $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();}); - $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();}); + this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded)); // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel if ($.fn.mousewheel) { results.mousewheel(function (e, delta, deltaX, deltaY) { - var top = results.scrollTop(); + var top = results.scrollTop(), height; if (deltaY > 0 && top - deltaY <= 0) { results.scrollTop(0); killEvent(e); @@ -738,21 +618,24 @@ the specific language governing permissions and limitations under the Apache Lic } installKeyUpChangeEvent(search); - search.on("keyup-change input paste", this.bind(this.updateResults)); - search.on("focus", function () { search.addClass("select2-focused"); }); - search.on("blur", function () { search.removeClass("select2-focused");}); + search.bind("keyup-change", this.bind(this.updateResults)); + search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); }); + search.bind("blur", function () { search.removeClass("select2-focused");}); - this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) { - if ($(e.target).closest(".select2-result-selectable").length > 0) { + this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) { + if ($(e.target).closest(".select2-result-selectable:not(.select2-disabled)").length > 0) { this.highlightUnderEvent(e); this.selectHighlighted(e); + } else { + this.focusSearch(); } + killEvent(e); })); // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's // dom it will trigger the popup close, which is not what we want - this.dropdown.on("click mouseup mousedown", function (e) { e.stopPropagation(); }); + this.dropdown.bind("click mouseup mousedown", function (e) { e.stopPropagation(); }); if ($.isFunction(this.opts.initSelection)) { // initialize selection based on the current value of the source element @@ -763,77 +646,25 @@ the specific language governing permissions and limitations under the Apache Lic this.monitorSource(); } - if (opts.maximumInputLength !== null) { - this.search.attr("maxlength", opts.maximumInputLength); - } - - var disabled = opts.element.prop("disabled"); - if (disabled === undefined) disabled = false; - this.enable(!disabled); - - var readonly = opts.element.prop("readonly"); - if (readonly === undefined) readonly = false; - this.readonly(readonly); - - // Calculate size of scrollbar - scrollBarDimensions = scrollBarDimensions || measureScrollbar(); - - this.autofocus = opts.element.prop("autofocus"); - opts.element.prop("autofocus", false); - if (this.autofocus) this.focus(); - - this.nextSearchTerm = undefined; + if (opts.element.is(":disabled") || opts.element.is("[readonly='readonly']")) this.disable(); }, // abstract destroy: function () { - var element=this.opts.element, select2 = element.data("select2"); - - this.close(); - - if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; } - + var select2 = this.opts.element.data("select2"); if (select2 !== undefined) { select2.container.remove(); select2.dropdown.remove(); - element - .removeClass("select2-offscreen") + select2.opts.element .removeData("select2") - .off(".select2") - .prop("autofocus", this.autofocus || false); - if (this.elementTabIndex) { - element.attr({tabindex: this.elementTabIndex}); - } else { - element.removeAttr("tabindex"); - } - element.show(); - } - }, - - // abstract - optionToData: function(element) { - if (element.is("option")) { - return { - id:element.prop("value"), - text:element.text(), - element: element.get(), - css: element.attr("class"), - disabled: element.prop("disabled"), - locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true) - }; - } else if (element.is("optgroup")) { - return { - text:element.attr("label"), - children:[], - element: element.get(), - css: element.attr("class") - }; + .unbind(".select2") + .show(); } }, // abstract prepareOpts: function (opts) { - var element, select, idKey, ajaxUrl, self = this; + var element, select, idKey, ajaxUrl; element = opts.element; @@ -852,37 +683,30 @@ the specific language governing permissions and limitations under the Apache Lic opts = $.extend({}, { populateResults: function(container, results, query) { - var populate, id=this.opts.id; + var populate, data, result, children, id=this.opts.id, self=this; populate=function(results, container, depth) { - var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted; - - results = opts.sortResults(results, container, query); - + var i, l, result, selectable, compound, node, label, innerContainer, formatted; for (i = 0, l = results.length; i < l; i = i + 1) { result=results[i]; - - disabled = (result.disabled === true); - selectable = (!disabled) && (id(result) !== undefined); - - compound=result.children && result.children.length > 0; + selectable=id(result) !== undefined; + compound=("children" in result) && result.children.length > 0; node=$("
  • "); node.addClass("select2-results-dept-"+depth); node.addClass("select2-result"); node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable"); - if (disabled) { node.addClass("select2-disabled"); } if (compound) { node.addClass("select2-result-with-children"); } node.addClass(self.opts.formatResultCssClass(result)); - label=$(document.createElement("div")); + label=$("
    "); label.addClass("select2-result-label"); - formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup); + formatted=opts.formatResult(result, label, query); if (formatted!==undefined) { - label.html(formatted); + label.html(self.opts.escapeMarkup(formatted)); } node.append(label); @@ -909,27 +733,20 @@ the specific language governing permissions and limitations under the Apache Lic opts.id = function (e) { return e[idKey]; }; } - if ($.isArray(opts.element.data("select2Tags"))) { - if ("tags" in opts) { - throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id"); - } - opts.tags=opts.element.data("select2Tags"); - } - if (select) { opts.query = this.bind(function (query) { var data = { results: [], more: false }, term = query.term, - children, placeholderOption, process; + children, firstChild, process; process=function(element, collection) { var group; if (element.is("option")) { if (query.matcher(term, element.text(), element)) { - collection.push(self.optionToData(element)); + collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class")}); } } else if (element.is("optgroup")) { - group=self.optionToData(element); + group={text:element.attr("label"), children:[], element: element.get(), css: element.attr("class")}; element.children().each2(function(i, elm) { process(elm, group.children); }); if (group.children.length>0) { collection.push(group); @@ -941,9 +758,9 @@ the specific language governing permissions and limitations under the Apache Lic // ignore the placeholder option if there is one if (this.getPlaceholder() !== undefined && children.length > 0) { - placeholderOption = this.getPlaceholderOption(); - if (placeholderOption) { - children=children.not(placeholderOption); + firstChild = children[0]; + if ($(firstChild).text() === "") { + children=children.not(firstChild); } } @@ -953,37 +770,31 @@ the specific language governing permissions and limitations under the Apache Lic }); // this is needed because inside val() we construct choices from options and there id is hardcoded opts.id=function(e) { return e.id; }; - opts.formatResultCssClass = function(data) { return data.css; }; + opts.formatResultCssClass = function(data) { return data.css; } } else { if (!("query" in opts)) { - if ("ajax" in opts) { ajaxUrl = opts.element.data("ajax-url"); if (ajaxUrl && ajaxUrl.length > 0) { opts.ajax.url = ajaxUrl; } - opts.query = ajax.call(opts.element, opts.ajax); + opts.query = ajax(opts.ajax); } else if ("data" in opts) { opts.query = local(opts.data); } else if ("tags" in opts) { opts.query = tags(opts.tags); - if (opts.createSearchChoice === undefined) { - opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; }; - } - if (opts.initSelection === undefined) { - opts.initSelection = function (element, callback) { - var data = []; - $(splitVal(element.val(), opts.separator)).each(function () { - var obj = { id: this, text: this }, - tags = opts.tags; - if ($.isFunction(tags)) tags=tags(); - $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } }); - data.push(obj); - }); - - callback(data); - }; - } + opts.createSearchChoice = function (term) { return {id: term, text: term}; }; + opts.initSelection = function (element, callback) { + var data = []; + $(splitVal(element.val(), opts.separator)).each(function () { + var id = this, text = this, tags=opts.tags; + if ($.isFunction(tags)) tags=tags(); + $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } }); + data.push({id: id, text: text}); + }); + + callback(data); + }; } } } @@ -999,57 +810,11 @@ the specific language governing permissions and limitations under the Apache Lic */ // abstract monitorSource: function () { - var el = this.opts.element, sync, observer; - - el.on("change.select2", this.bind(function (e) { + this.opts.element.bind("change.select2", this.bind(function (e) { if (this.opts.element.data("select2-change-triggered") !== true) { this.initSelection(); } })); - - sync = this.bind(function () { - - // sync enabled state - var disabled = el.prop("disabled"); - if (disabled === undefined) disabled = false; - this.enable(!disabled); - - var readonly = el.prop("readonly"); - if (readonly === undefined) readonly = false; - this.readonly(readonly); - - syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); - this.container.addClass(evaluate(this.opts.containerCssClass)); - - syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass); - this.dropdown.addClass(evaluate(this.opts.dropdownCssClass)); - - }); - - // IE8-10 - el.on("propertychange.select2", sync); - - // hold onto a reference of the callback to work around a chromium bug - if (this.mutationCallback === undefined) { - this.mutationCallback = function (mutations) { - mutations.forEach(sync); - } - } - - // safari, chrome, firefox, IE11 - observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver; - if (observer !== undefined) { - if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; } - this.propertyObserver = new observer(this.mutationCallback); - this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false }); - } - }, - - // abstract - triggerSelect: function(data) { - var evt = $.Event("select2-selecting", { val: this.id(data), object: data }); - this.opts.element.trigger(evt); - return !evt.isDefaultPrevented(); }, /** @@ -1075,50 +840,23 @@ the specific language governing permissions and limitations under the Apache Lic this.opts.element.blur(); }, - //abstract - isInterfaceEnabled: function() - { - return this.enabledInterface === true; - }, - - // abstract - enableInterface: function() { - var enabled = this._enabled && !this._readonly, - disabled = !enabled; - - if (enabled === this.enabledInterface) return false; - - this.container.toggleClass("select2-container-disabled", disabled); - this.close(); - this.enabledInterface = enabled; - - return true; - }, // abstract - enable: function(enabled) { - if (enabled === undefined) enabled = true; - if (this._enabled === enabled) return; - this._enabled = enabled; + enable: function() { + if (this.enabled) return; - this.opts.element.prop("disabled", !enabled); - this.enableInterface(); + this.enabled=true; + this.container.removeClass("select2-container-disabled"); }, // abstract disable: function() { - this.enable(false); - }, + if (!this.enabled) return; - // abstract - readonly: function(enabled) { - if (enabled === undefined) enabled = false; - if (this._readonly === enabled) return false; - this._readonly = enabled; + this.close(); - this.opts.element.prop("readonly", enabled); - this.enableInterface(); - return true; + this.enabled=false; + this.container.addClass("select2-container-disabled"); }, // abstract @@ -1128,107 +866,58 @@ the specific language governing permissions and limitations under the Apache Lic // abstract positionDropdown: function() { - var $dropdown = this.dropdown, - offset = this.container.offset(), - height = this.container.outerHeight(false), - width = this.container.outerWidth(false), - dropHeight = $dropdown.outerHeight(false), - $window = $(window), - windowWidth = $window.width(), - windowHeight = $window.height(), - viewPortRight = $window.scrollLeft() + windowWidth, - viewportBottom = $window.scrollTop() + windowHeight, + var offset = this.container.offset(), + height = this.container.outerHeight(), + width = this.container.outerWidth(), + dropHeight = this.dropdown.outerHeight(), + viewportBottom = $(window).scrollTop() + document.documentElement.clientHeight, dropTop = offset.top + height, dropLeft = offset.left, enoughRoomBelow = dropTop + dropHeight <= viewportBottom, enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(), - dropWidth = $dropdown.outerWidth(false), - enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight, - aboveNow = $dropdown.hasClass("select2-drop-above"), + aboveNow = this.dropdown.hasClass("select2-drop-above"), bodyOffset, above, - changeDirection, - css, - resultsListNode; - - // always prefer the current above/below alignment, unless there is not enough room - if (aboveNow) { - above = true; - if (!enoughRoomAbove && enoughRoomBelow) { - changeDirection = true; - above = false; - } - } else { - above = false; - if (!enoughRoomBelow && enoughRoomAbove) { - changeDirection = true; - above = true; - } - } - - //if we are changing direction we need to get positions when dropdown is hidden; - if (changeDirection) { - $dropdown.hide(); - offset = this.container.offset(); - height = this.container.outerHeight(false); - width = this.container.outerWidth(false); - dropHeight = $dropdown.outerHeight(false); - viewPortRight = $window.scrollLeft() + windowWidth; - viewportBottom = $window.scrollTop() + windowHeight; - dropTop = offset.top + height; - dropLeft = offset.left; - dropWidth = $dropdown.outerWidth(false); - enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight; - $dropdown.show(); - } - - if (this.opts.dropdownAutoWidth) { - resultsListNode = $('.select2-results', $dropdown)[0]; - $dropdown.addClass('select2-drop-auto-width'); - $dropdown.css('width', ''); - // Add scrollbar width to dropdown if vertical scrollbar is present - dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width); - dropWidth > width ? width = dropWidth : dropWidth = width; - enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight; - } - else { - this.container.removeClass('select2-drop-auto-width'); - } + css; - //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); - //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove); + // console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); + // console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove); // fix positioning when body has an offset and is not position: static + if (this.body().css('position') !== 'static') { bodyOffset = this.body().offset(); dropTop -= bodyOffset.top; dropLeft -= bodyOffset.left; } - if (!enoughRoomOnRight) { - dropLeft = offset.left + width - dropWidth; - } + // always prefer the current above/below alignment, unless there is not enough room - css = { - left: dropLeft, - width: width - }; + if (aboveNow) { + above = true; + if (!enoughRoomAbove && enoughRoomBelow) above = false; + } else { + above = false; + if (!enoughRoomBelow && enoughRoomAbove) above = true; + } if (above) { - css.bottom = windowHeight - offset.top; - css.top = 'auto'; + dropTop = offset.top - dropHeight; this.container.addClass("select2-drop-above"); - $dropdown.addClass("select2-drop-above"); + this.dropdown.addClass("select2-drop-above"); } else { - css.top = dropTop; - css.bottom = 'auto'; this.container.removeClass("select2-drop-above"); - $dropdown.removeClass("select2-drop-above"); + this.dropdown.removeClass("select2-drop-above"); } - css = $.extend(css, evaluate(this.opts.dropdownCss)); - $dropdown.css(css); + css = $.extend({ + top: dropTop, + left: dropLeft, + width: width + }, evaluate(this.opts.dropdownCss)); + + this.dropdown.css(css); }, // abstract @@ -1237,9 +926,7 @@ the specific language governing permissions and limitations under the Apache Lic if (this.opened()) return false; - if (this._enabled === false || this._readonly === true) return false; - - event = $.Event("select2-opening"); + event = jQuery.Event("open"); this.opts.element.trigger(event); return !event.isDefaultPrevented(); }, @@ -1262,7 +949,7 @@ the specific language governing permissions and limitations under the Apache Lic if (!this.shouldOpen()) return false; - this.opening(); + window.setTimeout(this.bind(this.opening), 1); return true; }, @@ -1272,105 +959,68 @@ the specific language governing permissions and limitations under the Apache Lic */ // abstract opening: function() { - var cid = this.containerId, - scroll = "scroll." + cid, - resize = "resize."+cid, - orient = "orientationchange."+cid, - mask; + var cid = this.containerId, selector = this.containerSelector, + scroll = "scroll." + cid, resize = "resize." + cid; + + this.container.parents().each(function() { + $(this).bind(scroll, function() { + var s2 = $(selector); + if (s2.length == 0) { + $(this).unbind(scroll); + } + s2.select2("close"); + }); + }); - this.container.addClass("select2-dropdown-open").addClass("select2-container-active"); + $(window).bind(resize, function() { + var s2 = $(selector); + if (s2.length == 0) { + $(window).unbind(resize); + } + s2.select2("close"); + }); this.clearDropdownAlignmentPreference(); - if(this.dropdown[0] !== this.body().children().last()[0]) { - this.dropdown.detach().appendTo(this.body()); - } - - // create the dropdown mask if doesnt already exist - mask = $("#select2-drop-mask"); - if (mask.length == 0) { - mask = $(document.createElement("div")); - mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask"); - mask.hide(); - mask.appendTo(this.body()); - mask.on("mousedown touchstart click", function (e) { - var dropdown = $("#select2-drop"), self; - if (dropdown.length > 0) { - self=dropdown.data("select2"); - if (self.opts.selectOnBlur) { - self.selectHighlighted({noFocus: true}); - } - self.close({focus:true}); - e.preventDefault(); - e.stopPropagation(); - } - }); - } + if (this.search.val() === " ") { this.search.val(""); } - // ensure the mask is always right before the dropdown - if (this.dropdown.prev()[0] !== mask[0]) { - this.dropdown.before(mask); - } + this.container.addClass("select2-dropdown-open").addClass("select2-container-active"); - // move the global id to the correct dropdown - $("#select2-drop").removeAttr("id"); - this.dropdown.attr("id", "select2-drop"); + this.updateResults(true); - // show the elements - mask.show(); + if(this.dropdown[0] !== this.body().children().last()[0]) { + this.dropdown.detach().appendTo(this.body()); + } - this.positionDropdown(); this.dropdown.show(); - this.positionDropdown(); + this.positionDropdown(); this.dropdown.addClass("select2-drop-active"); - // attach listeners to events that can change the position of the container and thus require - // the position of the dropdown to be updated as well so it does not come unglued from the container - var that = this; - this.container.parents().add(window).each(function () { - $(this).on(resize+" "+scroll+" "+orient, function (e) { - that.positionDropdown(); - }); - }); - + this.ensureHighlightVisible(); + this.focusSearch(); }, // abstract close: function () { if (!this.opened()) return; - var cid = this.containerId, - scroll = "scroll." + cid, - resize = "resize."+cid, - orient = "orientationchange."+cid; + var self = this; - // unbind event listeners - this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); }); + this.container.parents().each(function() { + $(this).unbind("scroll." + self.containerId); + }); + $(window).unbind("resize." + this.containerId); this.clearDropdownAlignmentPreference(); - $("#select2-drop-mask").hide(); - this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id this.dropdown.hide(); this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"); this.results.empty(); - - this.clearSearch(); - this.search.removeClass("select2-active"); - this.opts.element.trigger($.Event("select2-close")); - }, - /** - * Opens control, sets input value, and updates results. - */ - // abstract - externalSearch: function (term) { - this.open(); - this.search.val(term); - this.updateResults(false); + this.opts.element.trigger(jQuery.Event("close")); }, // abstract @@ -1378,11 +1028,6 @@ the specific language governing permissions and limitations under the Apache Lic }, - //abstract - getMaximumSelectionSize: function() { - return evaluate(this.opts.maximumSelectionSize); - }, - // abstract ensureHighlightVisible: function () { var results = this.results, children, index, child, hb, rb, y, more; @@ -1401,46 +1046,41 @@ the specific language governing permissions and limitations under the Apache Lic return; } - children = this.findHighlightableChoices().find('.select2-result-label'); + children = results.find(".select2-result-selectable"); child = $(children[index]); - hb = child.offset().top + child.outerHeight(true); + hb = child.offset().top + child.outerHeight(); // if this is the last child lets also make sure select2-more-results is visible if (index === children.length - 1) { more = results.find("li.select2-more-results"); if (more.length > 0) { - hb = more.offset().top + more.outerHeight(true); + hb = more.offset().top + more.outerHeight(); } } - rb = results.offset().top + results.outerHeight(true); + rb = results.offset().top + results.outerHeight(); if (hb > rb) { results.scrollTop(results.scrollTop() + (hb - rb)); } y = child.offset().top - results.offset().top; // make sure the top of the element is visible - if (y < 0 && child.css('display') != 'none' ) { + if (y < 0) { results.scrollTop(results.scrollTop() + y); // y is negative } }, - // abstract - findHighlightableChoices: function() { - return this.results.find(".select2-result-selectable:not(.select2-disabled, .select2-selected)"); - }, - // abstract moveHighlight: function (delta) { - var choices = this.findHighlightableChoices(), + var choices = this.results.find(".select2-result-selectable"), index = this.highlight(); while (index > -1 && index < choices.length) { index += delta; var choice = $(choices[index]); - if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) { + if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled")) { this.highlight(index); break; } @@ -1449,9 +1089,7 @@ the specific language governing permissions and limitations under the Apache Lic // abstract highlight: function (index) { - var choices = this.findHighlightableChoices(), - choice, - data; + var choices = this.results.find(".select2-result-selectable").not(".select2-disabled"); if (arguments.length === 0) { return indexOf(choices.filter(".select2-highlighted")[0], choices.get()); @@ -1460,37 +1098,27 @@ the specific language governing permissions and limitations under the Apache Lic if (index >= choices.length) index = choices.length - 1; if (index < 0) index = 0; - this.removeHighlight(); - - choice = $(choices[index]); - choice.addClass("select2-highlighted"); + choices.removeClass("select2-highlighted"); + $(choices[index]).addClass("select2-highlighted"); this.ensureHighlightVisible(); - data = choice.data("select2-data"); - if (data) { - this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data }); - } - }, - - removeHighlight: function() { - this.results.find(".select2-highlighted").removeClass("select2-highlighted"); }, // abstract countSelectableResults: function() { - return this.findHighlightableChoices().length; + return this.results.find(".select2-result-selectable").not(".select2-disabled").length; }, // abstract highlightUnderEvent: function (event) { var el = $(event.target).closest(".select2-result-selectable"); if (el.length > 0 && !el.is(".select2-highlighted")) { - var choices = this.findHighlightableChoices(); + var choices = this.results.find('.select2-result-selectable'); this.highlight(choices.index(el)); } else if (el.length == 0) { - // if we are over an unselectable item remove all highlights - this.removeHighlight(); + // if we are over an unselectable item remove al highlights + this.results.find(".select2-highlighted").removeClass("select2-highlighted"); } }, @@ -1499,6 +1127,7 @@ the specific language governing permissions and limitations under the Apache Lic var results = this.results, more = results.find("li.select2-more-results"), below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible + offset = -1, // index of first element without data page = this.resultsPage + 1, self=this, term=this.search.val(), @@ -1507,10 +1136,9 @@ the specific language governing permissions and limitations under the Apache Lic if (more.length === 0) return; below = more.offset().top - results.offset().top - results.height(); - if (below <= this.opts.loadMorePadding) { + if (below <= 0) { more.addClass("select2-active"); this.opts.query({ - element: this.opts.element, term: term, page: page, context: context, @@ -1522,7 +1150,6 @@ the specific language governing permissions and limitations under the Apache Lic self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context}); - self.postprocessResults(data, false, false); if (data.more===true) { more.detach().appendTo(results).text(self.opts.formatLoadMore(page+1)); @@ -1532,8 +1159,6 @@ the specific language governing permissions and limitations under the Apache Lic } self.positionDropdown(); self.resultsPage = page; - self.context = data.context; - this.opts.element.trigger({ type: "select2-loaded", items: data }); })}); } }, @@ -1550,75 +1175,42 @@ the specific language governing permissions and limitations under the Apache Lic */ // abstract updateResults: function (initial) { - var search = this.search, - results = this.results, - opts = this.opts, - data, - self = this, - input, - term = search.val(), - lastTerm = $.data(this.container, "select2-last-term"), - // sequence number used to drop out-of-order responses - queryNumber; - - // prevent duplicate queries against the same term - if (initial !== true && lastTerm && equal(term, lastTerm)) return; - - $.data(this.container, "select2-last-term", term); + var search = this.search, results = this.results, opts = this.opts, data, self=this, input; // if the search is currently hidden we do not alter the results if (initial !== true && (this.showSearchInput === false || !this.opened())) { return; } + search.addClass("select2-active"); + function postRender() { + results.scrollTop(0); search.removeClass("select2-active"); self.positionDropdown(); } function render(html) { - results.html(html); + results.html(self.opts.escapeMarkup(html)); postRender(); } - queryNumber = ++this.queryCount; - - var maxSelSize = this.getMaximumSelectionSize(); - if (maxSelSize >=1) { + if (opts.maximumSelectionSize >=1) { data = this.data(); - if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) { - render("
  • " + opts.formatSelectionTooBig(maxSelSize) + "
  • "); - return; - } - } - - if (search.val().length < opts.minimumInputLength) { - if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) { - render("
  • " + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "
  • "); - } else { - render(""); + if ($.isArray(data) && data.length >= opts.maximumSelectionSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) { + render("
  • " + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "
  • "); + return; } - if (initial && this.showSearch) this.showSearch(true); - return; } - if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) { - if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) { - render("
  • " + opts.formatInputTooLong(search.val(), opts.maximumInputLength) + "
  • "); - } else { - render(""); - } + if (search.val().length < opts.minimumInputLength && checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) { + render("
  • " + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "
  • "); return; } - - if (opts.formatSearching && this.findHighlightableChoices().length === 0) { + else { render("
  • " + opts.formatSearching() + "
  • "); } - search.addClass("select2-active"); - - this.removeHighlight(); - // give the tokenizer a chance to pre-process the input input = this.tokenize(); if (input != undefined && input != null) { @@ -1626,9 +1218,7 @@ the specific language governing permissions and limitations under the Apache Lic } this.resultsPage = 1; - opts.query({ - element: opts.element, term: search.val(), page: this.resultsPage, context: null, @@ -1636,22 +1226,15 @@ the specific language governing permissions and limitations under the Apache Lic callback: this.bind(function (data) { var def; // default choice - // ignore old responses - if (queryNumber != this.queryCount) { - return; - } - // ignore a response if the select2 has been closed before it was received - if (!this.opened()) { - this.search.removeClass("select2-active"); - return; - } + if (!this.opened()) return; // save context, if any this.context = (data.context===undefined) ? null : data.context; + // create a default choice and prepend it to the list if (this.opts.createSearchChoice && search.val() !== "") { - def = this.opts.createSearchChoice.call(self, search.val(), data.results); + def = this.opts.createSearchChoice.call(null, search.val(), data.results); if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) { if ($(data.results).filter( function () { @@ -1678,8 +1261,6 @@ the specific language governing permissions and limitations under the Apache Lic this.postprocessResults(data, initial); postRender(); - - this.opts.element.trigger({ type: "select2-loaded", items: data }); })}); }, @@ -1690,12 +1271,9 @@ the specific language governing permissions and limitations under the Apache Lic // abstract blur: function () { - // if selectOnBlur == true, select the currently highlighted option - if (this.opts.selectOnBlur) - this.selectHighlighted({noFocus: true}); - this.close(); this.container.removeClass("select2-container-active"); + this.dropdown.removeClass("select2-drop-active"); // synonymous to .is(':focus'), which is available in jquery >= 1.6 if (this.search[0] === document.activeElement) { this.search.blur(); } this.clearSearch(); @@ -1704,46 +1282,38 @@ the specific language governing permissions and limitations under the Apache Lic // abstract focusSearch: function () { - focus(this.search); + // need to do it here as well as in timeout so it works in IE + this.search.show(); + this.search.focus(); + + /* we do this in a timeout so that current event processing can complete before this code is executed. + this makes sure the search field is focussed even if the current event would blur it */ + window.setTimeout(this.bind(function () { + // reset the value so IE places the cursor at the end of the input box + this.search.show(); + this.search.focus(); + this.search.val(this.search.val()); + }), 10); }, // abstract - selectHighlighted: function (options) { + selectHighlighted: function () { var index=this.highlight(), - highlighted=this.results.find(".select2-highlighted"), - data = highlighted.closest('.select2-result').data("select2-data"); - + highlighted=this.results.find(".select2-highlighted").not(".select2-disabled"), + data = highlighted.closest('.select2-result-selectable').data("select2-data"); if (data) { + highlighted.addClass("select2-disabled"); this.highlight(index); - this.onSelect(data, options); - } else if (options && options.noFocus) { - this.close(); + this.onSelect(data); } }, // abstract getPlaceholder: function () { - var placeholderOption; return this.opts.element.attr("placeholder") || this.opts.element.attr("data-placeholder") || // jquery 1.4 compat this.opts.element.data("placeholder") || - this.opts.placeholder || - ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined); - }, - - // abstract - getPlaceholderOption: function() { - if (this.select) { - var firstOption = this.select.children('option').first(); - if (this.opts.placeholderOption !== undefined ) { - //Determine the placeholder option based on the specified placeholderOption setting - return (this.opts.placeholderOption === "first" && firstOption) || - (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select)); - } else if (firstOption.text() === "" && firstOption.val() === "") { - //No explicit placeholder option specified, use the first if it's blank - return firstOption; - } - } + this.opts.placeholder; }, /** @@ -1755,20 +1325,20 @@ the specific language governing permissions and limitations under the Apache Lic // abstract initContainerWidth: function () { function resolveContainerWidth() { - var style, attrs, matches, i, l, attr; + var style, attrs, matches, i, l; if (this.opts.width === "off") { return null; } else if (this.opts.width === "element"){ - return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px'; + return this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px'; } else if (this.opts.width === "copy" || this.opts.width === "resolve") { // check if there is inline style on the element that contains width style = this.opts.element.attr('style'); if (style !== undefined) { attrs = style.split(';'); for (i = 0, l = attrs.length; i < l; i = i + 1) { - attr = attrs[i].replace(/\s/g, ''); - matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i); + matches = attrs[i].replace(/\s/g, '') + .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/); if (matches !== null && matches.length >= 1) return matches[1]; } @@ -1781,7 +1351,7 @@ the specific language governing permissions and limitations under the Apache Lic if (style.indexOf("%") > 0) return style; // finally, fallback on the calculated width of the element - return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px'); + return (this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px'); } return null; @@ -1794,7 +1364,7 @@ the specific language governing permissions and limitations under the Apache Lic var width = resolveContainerWidth.call(this); if (width !== null) { - this.container.css("width", width); + this.container.attr("style", "width: "+width); } } }); @@ -1803,115 +1373,53 @@ the specific language governing permissions and limitations under the Apache Lic // single - createContainer: function () { - var container = $(document.createElement("div")).attr({ + createContainer: function () { + var container = $("
    ", { "class": "select2-container" }).html([ - "", - "  ", - " ", + " ", + " ", + "
    " , "
    ", - "", - "
    ", - " ", - "
      ", - "
    ", + "
    " , + " " , + "
      " , + "
    " , "
    "].join("")); return container; }, - // single - enableInterface: function() { - if (this.parent.enableInterface.apply(this, arguments)) { - this.focusser.prop("disabled", !this.isInterfaceEnabled()); - } - }, - // single opening: function () { - var el, range, len; - - if (this.opts.minimumResultsForSearch >= 0) { - this.showSearch(true); - } - + this.search.show(); this.parent.opening.apply(this, arguments); - - if (this.showSearchInput !== false) { - // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range - // all other browsers handle this just fine - - this.search.val(this.focusser.val()); - } - this.search.focus(); - // move the cursor to the end after focussing, otherwise it will be at the beginning and - // new text will appear *before* focusser.val() - el = this.search.get(0); - if (el.createTextRange) { - range = el.createTextRange(); - range.collapse(false); - range.select(); - } else if (el.setSelectionRange) { - len = this.search.val().length; - el.setSelectionRange(len, len); - } - - // initializes search's value with nextSearchTerm (if defined by user) - // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter - if(this.search.val() === "") { - if(this.nextSearchTerm != undefined){ - this.search.val(this.nextSearchTerm); - this.search.select(); - } - } - - this.focusser.prop("disabled", true).val(""); - this.updateResults(true); - this.opts.element.trigger($.Event("select2-open")); + this.dropdown.removeClass("select2-offscreen"); }, // single - close: function (params) { + close: function () { if (!this.opened()) return; this.parent.close.apply(this, arguments); - - params = params || {focus: true}; - this.focusser.removeAttr("disabled"); - - if (params.focus) { - this.focusser.focus(); - } + this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show(); }, // single focus: function () { - if (this.opened()) { - this.close(); - } else { - this.focusser.removeAttr("disabled"); - this.focusser.focus(); - } + this.close(); + this.selection.focus(); }, // single isFocused: function () { - return this.container.hasClass("select2-container-active"); + return this.selection[0] === document.activeElement; }, // single cancel: function () { this.parent.cancel.apply(this, arguments); - this.focusser.removeAttr("disabled"); - this.focusser.focus(); - }, - - // single - destroy: function() { - $("label[for='" + this.focusser.attr('id') + "']") - .attr('for', this.opts.element.attr("id")); - this.parent.destroy.apply(this, arguments); + this.selection.focus(); }, // single @@ -1919,28 +1427,13 @@ the specific language governing permissions and limitations under the Apache Lic var selection, container = this.container, - dropdown = this.dropdown; - - if (this.opts.minimumResultsForSearch < 0) { - this.showSearch(false); - } else { - this.showSearch(true); - } + dropdown = this.dropdown, + clickingInside = false; this.selection = selection = container.find(".select2-choice"); - this.focusser = container.find(".select2-focusser"); - - // rewrite labels from original element to focusser - this.focusser.attr("id", "s2id_autogen"+nextUid()); - - $("label[for='" + this.opts.element.attr("id") + "']") - .attr('for', this.focusser.attr('id')); - - this.focusser.attr("tabindex", this.elementTabIndex); - - this.search.on("keydown", this.bind(function (e) { - if (!this.isInterfaceEnabled()) return; + this.search.bind("keydown", this.bind(function (e) { + if (!this.enabled) return; if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { // prevent the page from scrolling @@ -1948,150 +1441,154 @@ the specific language governing permissions and limitations under the Apache Lic return; } - switch (e.which) { - case KEY.UP: - case KEY.DOWN: - this.moveHighlight((e.which === KEY.UP) ? -1 : 1); - killEvent(e); - return; - case KEY.ENTER: - this.selectHighlighted(); - killEvent(e); + if (this.opened()) { + switch (e.which) { + case KEY.UP: + case KEY.DOWN: + this.moveHighlight((e.which === KEY.UP) ? -1 : 1); + killEvent(e); + return; + case KEY.TAB: + case KEY.ENTER: + this.selectHighlighted(); + killEvent(e); + return; + case KEY.ESC: + this.cancel(e); + killEvent(e); + return; + } + } else { + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { return; - case KEY.TAB: - this.selectHighlighted({noFocus: true}); + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { return; - case KEY.ESC: - this.cancel(e); - killEvent(e); + } + + this.open(); + + if (e.which === KEY.ENTER) { + // do not propagate the event otherwise we open, and propagate enter which closes return; + } } })); - this.search.on("blur", this.bind(function(e) { - // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown. - // without this the search field loses focus which is annoying - if (document.activeElement === this.body().get(0)) { - window.setTimeout(this.bind(function() { - this.search.focus(); - }), 0); + this.search.bind("focus", this.bind(function() { + this.selection.attr("tabIndex", "-1"); + })); + this.search.bind("blur", this.bind(function() { + if (!this.opened()) this.container.removeClass("select2-container-active"); + window.setTimeout(this.bind(function() { this.selection.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); + })); + + selection.bind("mousedown", this.bind(function (e) { + clickingInside = true; + + if (this.opened()) { + this.close(); + this.selection.focus(); + } else if (this.enabled) { + this.open(); } + + clickingInside = false; })); - this.focusser.on("keydown", this.bind(function (e) { - if (!this.isInterfaceEnabled()) return; + dropdown.bind("mousedown", this.bind(function() { this.search.focus(); })); - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { - return; + selection.bind("focus", this.bind(function() { + this.container.addClass("select2-container-active"); + // hide the search so the tab key does not focus on it + this.search.attr("tabIndex", "-1"); + })); + + selection.bind("blur", this.bind(function() { + if (!this.opened()) { + this.container.removeClass("select2-container-active"); } + window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); + })); - if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + selection.bind("keydown", this.bind(function(e) { + if (!this.enabled) return; + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling killEvent(e); return; } - if (e.which == KEY.DOWN || e.which == KEY.UP - || (e.which == KEY.ENTER && this.opts.openOnEnter)) { - - if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return; + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) + || e.which === KEY.ESC) { + return; + } - this.open(); - killEvent(e); + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { return; } - if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) { + if (e.which == KEY.DELETE) { if (this.opts.allowClear) { this.clear(); } - killEvent(e); return; } - })); + this.open(); - installKeyUpChangeEvent(this.focusser); - this.focusser.on("keyup-change input", this.bind(function(e) { - if (this.opts.minimumResultsForSearch >= 0) { - e.stopPropagation(); - if (this.opened()) return; - this.open(); + if (e.which === KEY.ENTER) { + // do not propagate the event otherwise we open, and propagate enter which closes + killEvent(e); + return; } - })); - selection.on("mousedown", "abbr", this.bind(function (e) { - if (!this.isInterfaceEnabled()) return; - this.clear(); - killEventImmediately(e); - this.close(); - this.selection.focus(); - })); + // do not set the search input value for non-alpha-numeric keys + // otherwise pressing down results in a '(' being set in the search field + if (e.which < 48 ) { // '0' == 48 + killEvent(e); + return; + } - selection.on("mousedown", this.bind(function (e) { + var keyWritten = String.fromCharCode(e.which).toLowerCase(); - if (!this.container.hasClass("select2-container-active")) { - this.opts.element.trigger($.Event("select2-focus")); + if (e.shiftKey) { + keyWritten = keyWritten.toUpperCase(); } - if (this.opened()) { - this.close(); - } else if (this.isInterfaceEnabled()) { - this.open(); - } + // focus the field before calling val so the cursor ends up after the value instead of before + this.search.focus(); + this.search.val(keyWritten); + // prevent event propagation so it doesnt replay on the now focussed search field and result in double key entry killEvent(e); })); - dropdown.on("mousedown", this.bind(function() { this.search.focus(); })); - - selection.on("focus", this.bind(function(e) { + selection.delegate("abbr", "mousedown", this.bind(function (e) { + if (!this.enabled) return; + this.clear(); killEvent(e); + this.close(); + this.triggerChange(); + this.selection.focus(); })); - this.focusser.on("focus", this.bind(function(){ - if (!this.container.hasClass("select2-container-active")) { - this.opts.element.trigger($.Event("select2-focus")); - } - this.container.addClass("select2-container-active"); - })).on("blur", this.bind(function() { - if (!this.opened()) { - this.container.removeClass("select2-container-active"); - this.opts.element.trigger($.Event("select2-blur")); - } - })); - this.search.on("focus", this.bind(function(){ - if (!this.container.hasClass("select2-container-active")) { - this.opts.element.trigger($.Event("select2-focus")); - } - this.container.addClass("select2-container-active"); - })); - - this.initContainerWidth(); - this.opts.element.addClass("select2-offscreen"); this.setPlaceholder(); + this.search.bind("focus", this.bind(function() { + this.container.addClass("select2-container-active"); + })); }, // single - clear: function(triggerChange) { - var data=this.selection.data("select2-data"); - if (data) { // guard against queued quick consecutive clicks - var evt = $.Event("select2-clearing"); - this.opts.element.trigger(evt); - if (evt.isDefaultPrevented()) { - return; - } - var placeholderOption = this.getPlaceholderOption(); - this.opts.element.val(placeholderOption ? placeholderOption.val() : ""); - this.selection.find(".select2-chosen").empty(); - this.selection.removeData("select2-data"); - this.setPlaceholder(); - - if (triggerChange !== false){ - this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data }); - this.triggerChange({removed:data}); - } - } + clear: function() { + this.opts.element.val(""); + this.selection.find("span").empty(); + this.selection.removeData("select2-data"); + this.setPlaceholder(); }, /** @@ -2100,8 +1597,7 @@ the specific language governing permissions and limitations under the Apache Lic // single initSelection: function () { var selected; - if (this.isPlaceholderOptionSelected()) { - this.updateSelection(null); + if (this.opts.element.val() === "") { this.close(); this.setPlaceholder(); } else { @@ -2116,87 +1612,47 @@ the specific language governing permissions and limitations under the Apache Lic } }, - isPlaceholderOptionSelected: function() { - var placeholderOption; - if (!this.getPlaceholder()) return false; // no placeholder specified so no option should be considered - return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected")) - || (this.opts.element.val() === "") - || (this.opts.element.val() === undefined) - || (this.opts.element.val() === null); - }, - // single prepareOpts: function () { - var opts = this.parent.prepareOpts.apply(this, arguments), - self=this; + var opts = this.parent.prepareOpts.apply(this, arguments); if (opts.element.get(0).tagName.toLowerCase() === "select") { // install the selection initializer opts.initSelection = function (element, callback) { - var selected = element.find("option").filter(function() { return this.selected }); + var selected = element.find(":selected"); // a single select box always has a value, no need to null check 'selected' - callback(self.optionToData(selected)); - }; - } else if ("data" in opts) { - // install default initSelection when applied to hidden input and data is local - opts.initSelection = opts.initSelection || function (element, callback) { - var id = element.val(); - //search in data by id, storing the actual matching item - var match = null; - opts.query({ - matcher: function(term, text, el){ - var is_match = equal(id, opts.id(el)); - if (is_match) { - match = el; - } - return is_match; - }, - callback: !$.isFunction(callback) ? $.noop : function() { - callback(match); - } - }); + if ($.isFunction(callback)) + callback({id: selected.attr("value"), text: selected.text()}); }; } return opts; }, - // single - getPlaceholder: function() { - // if a placeholder is specified on a single select without a valid placeholder option ignore it - if (this.select) { - if (this.getPlaceholderOption() === undefined) { - return undefined; - } - } - - return this.parent.getPlaceholder.apply(this, arguments); - }, - // single setPlaceholder: function () { var placeholder = this.getPlaceholder(); - if (this.isPlaceholderOptionSelected() && placeholder !== undefined) { + if (this.opts.element.val() === "" && placeholder !== undefined) { - // check for a placeholder option if attached to a select - if (this.select && this.getPlaceholderOption() === undefined) return; + // check for a first blank option if attached to a select + if (this.select && this.select.find("option:first").text() !== "") return; - this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder)); + this.selection.find("span").html(this.opts.escapeMarkup(placeholder)); this.selection.addClass("select2-default"); - this.container.removeClass("select2-allowclear"); + this.selection.find("abbr").hide(); } }, // single - postprocessResults: function (data, initial, noHighlightUpdate) { + postprocessResults: function (data, initial) { var selected = 0, self = this, showSearchInput = true; // find the selected element in the result list - this.findHighlightableChoices().each2(function (i, elm) { + this.results.find(".select2-result-selectable").each2(function (i, elm) { if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) { selected = i; return false; @@ -2204,91 +1660,56 @@ the specific language governing permissions and limitations under the Apache Lic }); // and highlight it - if (noHighlightUpdate !== false) { - if (initial === true && selected >= 0) { - this.highlight(selected); - } else { - this.highlight(0); - } - } - // hide the search box if this is the first we got the results and there are enough of them for search + this.highlight(selected); - if (initial === true) { - var min = this.opts.minimumResultsForSearch; - if (min >= 0) { - this.showSearch(countResults(data.results) >= min); - } - } - }, + // hide the search box if this is the first we got the results and there are a few of them - // single - showSearch: function(showSearchInput) { - if (this.showSearchInput === showSearchInput) return; + if (initial === true) { + showSearchInput = this.showSearchInput = countResults(data.results) >= this.opts.minimumResultsForSearch; + this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden"); - this.showSearchInput = showSearchInput; + //add "select2-with-searchbox" to the container if search box is shown + $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox"); + } - this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput); - this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput); - //add "select2-with-searchbox" to the container if search box is shown - $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput); }, // single - onSelect: function (data, options) { - - if (!this.triggerSelect(data)) { return; } - - var old = this.opts.element.val(), - oldData = this.data(); + onSelect: function (data) { + var old = this.opts.element.val(); this.opts.element.val(this.id(data)); this.updateSelection(data); - - this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data }); - - this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val()); this.close(); + this.selection.focus(); - if (!options || !options.noFocus) - this.focusser.focus(); - - if (!equal(old, this.id(data))) { this.triggerChange({added:data,removed:oldData}); } + if (!equal(old, this.id(data))) { this.triggerChange(); } }, // single updateSelection: function (data) { - var container=this.selection.find(".select2-chosen"), formatted, cssClass; + var container=this.selection.find("span"), formatted; this.selection.data("select2-data", data); container.empty(); - if (data !== null) { - formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup); - } + formatted=this.opts.formatSelection(data, container); if (formatted !== undefined) { - container.append(formatted); - } - cssClass=this.opts.formatSelectionCssClass(data, container); - if (cssClass !== undefined) { - container.addClass(cssClass); + container.append(this.opts.escapeMarkup(formatted)); } this.selection.removeClass("select2-default"); if (this.opts.allowClear && this.getPlaceholder() !== undefined) { - this.container.addClass("select2-allowclear"); + this.selection.find("abbr").show(); } }, // single val: function () { - var val, - triggerChange = false, - data = null, - self = this, - oldData = this.data(); + var val, data = null, self = this; if (arguments.length === 0) { return this.opts.element.val(); @@ -2296,39 +1717,29 @@ the specific language governing permissions and limitations under the Apache Lic val = arguments[0]; - if (arguments.length > 1) { - triggerChange = arguments[1]; - } - if (this.select) { this.select .val(val) - .find("option").filter(function() { return this.selected }).each2(function (i, elm) { - data = self.optionToData(elm); + .find(":selected").each2(function (i, elm) { + data = {id: elm.attr("value"), text: elm.text()}; return false; }); this.updateSelection(data); this.setPlaceholder(); - if (triggerChange) { - this.triggerChange({added: data, removed:oldData}); - } } else { - // val is an id. !val is true for [undefined,null,'',0] - 0 is legal - if (!val && val !== 0) { - this.clear(triggerChange); - return; - } if (this.opts.initSelection === undefined) { throw new Error("cannot call val() if initSelection() is not defined"); } + // val is an id. !val is true for [undefined,null,''] + if (!val) { + this.clear(); + return; + } this.opts.element.val(val); this.opts.initSelection(this.opts.element, function(data){ self.opts.element.val(!data ? "" : self.id(data)); self.updateSelection(data); self.setPlaceholder(); - if (triggerChange) { - self.triggerChange({added: data, removed:oldData}); - } }); } }, @@ -2336,31 +1747,22 @@ the specific language governing permissions and limitations under the Apache Lic // single clearSearch: function () { this.search.val(""); - this.focusser.val(""); }, // single data: function(value) { - var data, - triggerChange = false; + var data; if (arguments.length === 0) { data = this.selection.data("select2-data"); if (data == undefined) data = null; return data; } else { - if (arguments.length > 1) { - triggerChange = arguments[1]; - } - if (!value) { - this.clear(triggerChange); + if (!value || value === "") { + this.clear(); } else { - data = this.data(); this.opts.element.val(!value ? "" : this.id(value)); this.updateSelection(value); - if (triggerChange) { - this.triggerChange({added: value, removed:data}); - } } } } @@ -2370,105 +1772,45 @@ the specific language governing permissions and limitations under the Apache Lic // multi createContainer: function () { - var container = $(document.createElement("div")).attr({ + var container = $("
    ", { "class": "select2-container select2-container-multi" }).html([ - "
      ", - "
    • ", - " ", - "
    • ", - "
    ", - "
    ", - "
      ", - "
    ", + "
      ", + //"
    • California
    • " , + "
    • " , + " " , + "
    • " , + "
    " , + ""].join("")); - return container; + return container; }, // multi prepareOpts: function () { - var opts = this.parent.prepareOpts.apply(this, arguments), - self=this; + var opts = this.parent.prepareOpts.apply(this, arguments); // TODO validate placeholder is a string if specified if (opts.element.get(0).tagName.toLowerCase() === "select") { // install sthe selection initializer - opts.initSelection = function (element, callback) { + opts.initSelection = function (element,callback) { var data = []; - - element.find("option").filter(function() { return this.selected }).each2(function (i, elm) { - data.push(self.optionToData(elm)); - }); - callback(data); - }; - } else if ("data" in opts) { - // install default initSelection when applied to hidden input and data is local - opts.initSelection = opts.initSelection || function (element, callback) { - var ids = splitVal(element.val(), opts.separator); - //search in data by array of ids, storing matching items in a list - var matches = []; - opts.query({ - matcher: function(term, text, el){ - var is_match = $.grep(ids, function(id) { - return equal(id, opts.id(el)); - }).length; - if (is_match) { - matches.push(el); - } - return is_match; - }, - callback: !$.isFunction(callback) ? $.noop : function() { - // reorder matches based on the order they appear in the ids array because right now - // they are in the order in which they appear in data array - var ordered = []; - for (var i = 0; i < ids.length; i++) { - var id = ids[i]; - for (var j = 0; j < matches.length; j++) { - var match = matches[j]; - if (equal(id, opts.id(match))) { - ordered.push(match); - matches.splice(j, 1); - break; - } - } - } - callback(ordered); - } + element.find(":selected").each2(function (i, elm) { + data.push({id: elm.attr("value"), text: elm.text()}); }); + + if ($.isFunction(callback)) + callback(data); }; } return opts; }, - // multi - selectChoice: function (choice) { - - var selected = this.container.find(".select2-search-choice-focus"); - if (selected.length && choice && choice[0] == selected[0]) { - - } else { - if (selected.length) { - this.opts.element.trigger("choice-deselected", selected); - } - selected.removeClass("select2-search-choice-focus"); - if (choice && choice.length) { - this.close(); - choice.addClass("select2-search-choice-focus"); - this.opts.element.trigger("choice-selected", choice); - } - } - }, - - // multi - destroy: function() { - $("label[for='" + this.search.attr('id') + "']") - .attr('for', this.opts.element.attr("id")); - this.parent.destroy.apply(this, arguments); - }, - // multi initContainer: function () { @@ -2477,72 +1819,27 @@ the specific language governing permissions and limitations under the Apache Lic this.searchContainer = this.container.find(".select2-search-field"); this.selection = selection = this.container.find(selector); - var _this = this; - this.selection.on("click", ".select2-search-choice:not(.select2-locked)", function (e) { - //killEvent(e); - _this.search[0].focus(); - _this.selectChoice($(this)); - }); - - // rewrite labels from original element to focusser - this.search.attr("id", "s2id_autogen"+nextUid()); - $("label[for='" + this.opts.element.attr("id") + "']") - .attr('for', this.search.attr('id')); - - this.search.on("input paste", this.bind(function() { - if (!this.isInterfaceEnabled()) return; - if (!this.opened()) { - this.open(); - } - })); - - this.search.attr("tabindex", this.elementTabIndex); + this.search.bind("keydown", this.bind(function (e) { + if (!this.enabled) return; - this.keydowns = 0; - this.search.on("keydown", this.bind(function (e) { - if (!this.isInterfaceEnabled()) return; - - ++this.keydowns; - var selected = selection.find(".select2-search-choice-focus"); - var prev = selected.prev(".select2-search-choice:not(.select2-locked)"); - var next = selected.next(".select2-search-choice:not(.select2-locked)"); - var pos = getCursorInfo(this.search); + if (e.which === KEY.BACKSPACE && this.search.val() === "") { + this.close(); - if (selected.length && - (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) { - var selectedChoice = selected; - if (e.which == KEY.LEFT && prev.length) { - selectedChoice = prev; - } - else if (e.which == KEY.RIGHT) { - selectedChoice = next.length ? next : null; - } - else if (e.which === KEY.BACKSPACE) { - this.unselect(selected.first()); - this.search.width(10); - selectedChoice = prev.length ? prev : next; - } else if (e.which == KEY.DELETE) { + var choices, + selected = selection.find(".select2-search-choice-focus"); + if (selected.length > 0) { this.unselect(selected.first()); this.search.width(10); - selectedChoice = next.length ? next : null; - } else if (e.which == KEY.ENTER) { - selectedChoice = null; + killEvent(e); + return; } - this.selectChoice(selectedChoice); - killEvent(e); - if (!selectedChoice || !selectedChoice.length) { - this.open(); + choices = selection.find(".select2-search-choice"); + if (choices.length > 0) { + choices.last().addClass("select2-search-choice-focus"); } - return; - } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1) - || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) { - - this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last()); - killEvent(e); - return; } else { - this.selectChoice(null); + selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); } if (this.opened()) { @@ -2553,13 +1850,10 @@ the specific language governing permissions and limitations under the Apache Lic killEvent(e); return; case KEY.ENTER: + case KEY.TAB: this.selectHighlighted(); killEvent(e); return; - case KEY.TAB: - this.selectHighlighted({noFocus:true}); - this.close(); - return; case KEY.ESC: this.cancel(e); killEvent(e); @@ -2572,12 +1866,8 @@ the specific language governing permissions and limitations under the Apache Lic return; } - if (e.which === KEY.ENTER) { - if (this.opts.openOnEnter === false) { - return; - } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { - return; - } + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + return; } this.open(); @@ -2586,73 +1876,62 @@ the specific language governing permissions and limitations under the Apache Lic // prevent the page from scrolling killEvent(e); } - - if (e.which === KEY.ENTER) { - // prevent form from being submitted - killEvent(e); - } - })); - this.search.on("keyup", this.bind(function (e) { - this.keydowns = 0; - this.resizeSearch(); - }) - ); + this.search.bind("keyup", this.bind(this.resizeSearch)); - this.search.on("blur", this.bind(function(e) { + this.search.bind("blur", this.bind(function(e) { this.container.removeClass("select2-container-active"); this.search.removeClass("select2-focused"); - this.selectChoice(null); - if (!this.opened()) this.clearSearch(); + this.clearSearch(); e.stopImmediatePropagation(); - this.opts.element.trigger($.Event("select2-blur")); })); - this.container.on("click", selector, this.bind(function (e) { - if (!this.isInterfaceEnabled()) return; + this.container.delegate(selector, "mousedown", this.bind(function (e) { + if (!this.enabled) return; if ($(e.target).closest(".select2-search-choice").length > 0) { // clicked inside a select2 search choice, do not open return; } - this.selectChoice(null); this.clearPlaceholder(); - if (!this.container.hasClass("select2-container-active")) { - this.opts.element.trigger($.Event("select2-focus")); - } this.open(); this.focusSearch(); e.preventDefault(); })); - this.container.on("focus", selector, this.bind(function () { - if (!this.isInterfaceEnabled()) return; - if (!this.container.hasClass("select2-container-active")) { - this.opts.element.trigger($.Event("select2-focus")); - } + this.container.delegate(selector, "focus", this.bind(function () { + if (!this.enabled) return; this.container.addClass("select2-container-active"); this.dropdown.addClass("select2-drop-active"); this.clearPlaceholder(); })); - this.initContainerWidth(); - this.opts.element.addClass("select2-offscreen"); - // set the placeholder if necessary this.clearSearch(); }, // multi - enableInterface: function() { - if (this.parent.enableInterface.apply(this, arguments)) { - this.search.prop("disabled", !this.isInterfaceEnabled()); - } + enable: function() { + if (this.enabled) return; + + this.parent.enable.apply(this, arguments); + + this.search.removeAttr("disabled"); + }, + + // multi + disable: function() { + if (!this.enabled) return; + + this.parent.disable.apply(this, arguments); + + this.search.attr("disabled", true); }, // multi initSelection: function () { var data; - if (this.opts.element.val() === "" && this.opts.element.text() === "") { + if (this.opts.element.val() === "") { this.updateSelection([]); this.close(); // set the placeholder if necessary @@ -2673,16 +1952,16 @@ the specific language governing permissions and limitations under the Apache Lic // multi clearSearch: function () { - var placeholder = this.getPlaceholder(), - maxWidth = this.getMaxSearchWidth(); + var placeholder = this.getPlaceholder(); if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) { this.search.val(placeholder).addClass("select2-default"); // stretch the search box to full width of the container so as much of the placeholder is visible as possible - // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944 - this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width")); + this.resizeSearch(); } else { - this.search.val("").width(10); + // we set this to " " instead of "" and later clear it on focus() because there is a firefox bug + // that does not properly render the caret when the field starts out blank + this.search.val(" ").width(10); } }, @@ -2690,21 +1969,19 @@ the specific language governing permissions and limitations under the Apache Lic clearPlaceholder: function () { if (this.search.hasClass("select2-default")) { this.search.val("").removeClass("select2-default"); + } else { + // work around for the space character we set to avoid firefox caret bug + if (this.search.val() === " ") this.search.val(""); } }, // multi opening: function () { - this.clearPlaceholder(); // should be done before super so placeholder is not used to search - this.resizeSearch(); - this.parent.opening.apply(this, arguments); + this.clearPlaceholder(); + this.resizeSearch(); this.focusSearch(); - - this.updateResults(true); - this.search.focus(); - this.opts.element.trigger($.Event("select2-open")); }, // multi @@ -2744,10 +2021,9 @@ the specific language governing permissions and limitations under the Apache Lic self.postprocessResults(); }, - // multi tokenize: function() { var input = this.search.val(); - input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts); + input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts); if (input != null && input != undefined) { this.search.val(input); if (input.length > 0) { @@ -2758,15 +2034,9 @@ the specific language governing permissions and limitations under the Apache Lic }, // multi - onSelect: function (data, options) { - - if (!this.triggerSelect(data)) { return; } - + onSelect: function (data) { this.addSelectedChoice(data); - - this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data }); - - if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true); + if (this.select) { this.postprocessResults(); } if (this.opts.closeOnSelect) { this.close(); @@ -2775,16 +2045,10 @@ the specific language governing permissions and limitations under the Apache Lic if (this.countSelectableResults()>0) { this.search.width(10); this.resizeSearch(); - if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) { - // if we reached max selection size repaint the results so choices - // are replaced with the max selection reached message - this.updateResults(true); - } this.positionDropdown(); } else { // if nothing left to select close this.close(); - this.search.width(10); } } @@ -2792,8 +2056,7 @@ the specific language governing permissions and limitations under the Apache Lic // added we do not need to check if this is a new element before firing change this.triggerChange({ added: data }); - if (!options || !options.noFocus) - this.focusSearch(); + this.focusSearch(); }, // multi @@ -2802,51 +2065,36 @@ the specific language governing permissions and limitations under the Apache Lic this.focusSearch(); }, + // multi addSelectedChoice: function (data) { - var enableChoice = !data.locked, - enabledItem = $( + var choice=$( "
  • " + "
    " + " " + "
  • "), - disabledItem = $( - "
  • " + - "
    " + - "
  • "); - var choice = enableChoice ? enabledItem : disabledItem, id = this.id(data), val = this.getVal(), - formatted, - cssClass; - - formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup); - if (formatted != undefined) { - choice.find("div").replaceWith("
    "+formatted+"
    "); - } - cssClass=this.opts.formatSelectionCssClass(data, choice.find("div")); - if (cssClass != undefined) { - choice.addClass(cssClass); - } - - if(enableChoice){ - choice.find(".select2-search-choice-close") - .on("mousedown", killEvent) - .on("click dblclick", this.bind(function (e) { - if (!this.isInterfaceEnabled()) return; - - $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){ - this.unselect($(e.target)); - this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); - this.close(); - this.focusSearch(); - })).dequeue(); - killEvent(e); - })).on("focus", this.bind(function () { - if (!this.isInterfaceEnabled()) return; - this.container.addClass("select2-container-active"); - this.dropdown.addClass("select2-drop-active"); - })); - } + formatted; + + formatted=this.opts.formatSelection(data, choice); + choice.find("div").replaceWith("
    "+this.opts.escapeMarkup(formatted)+"
    "); + choice.find(".select2-search-choice-close") + .bind("mousedown", killEvent) + .bind("click dblclick", this.bind(function (e) { + if (!this.enabled) return; + + $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){ + this.unselect($(e.target)); + this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + this.close(); + this.focusSearch(); + })).dequeue(); + killEvent(e); + })).bind("focus", this.bind(function () { + if (!this.enabled) return; + this.container.addClass("select2-container-active"); + this.dropdown.addClass("select2-drop-active"); + })); choice.data("select2-data", data); choice.insertBefore(this.searchContainer); @@ -2860,6 +2108,7 @@ the specific language governing permissions and limitations under the Apache Lic var val = this.getVal(), data, index; + selected = selected.closest(".select2-search-choice"); if (selected.length === 0) { @@ -2868,81 +2117,55 @@ the specific language governing permissions and limitations under the Apache Lic data = selected.data("select2-data"); - if (!data) { - // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued - // and invoked on an element already removed - return; - } + index = indexOf(this.id(data), val); - while((index = indexOf(this.id(data), val)) >= 0) { + if (index >= 0) { val.splice(index, 1); this.setVal(val); if (this.select) this.postprocessResults(); } - - var evt = $.Event("select2-removing"); - evt.val = this.id(data); - evt.choice = data; - this.opts.element.trigger(evt); - - if (evt.isDefaultPrevented()) { - return; - } - selected.remove(); - - this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data }); this.triggerChange({ removed: data }); }, // multi - postprocessResults: function (data, initial, noHighlightUpdate) { + postprocessResults: function () { var val = this.getVal(), - choices = this.results.find(".select2-result"), + choices = this.results.find(".select2-result-selectable"), compound = this.results.find(".select2-result-with-children"), self = this; choices.each2(function (i, choice) { var id = self.id(choice.data("select2-data")); if (indexOf(id, val) >= 0) { - choice.addClass("select2-selected"); - // mark all children of the selected parent as selected - choice.find(".select2-result-selectable").addClass("select2-selected"); + choice.addClass("select2-disabled").removeClass("select2-result-selectable"); + } else { + choice.removeClass("select2-disabled").addClass("select2-result-selectable"); } }); - compound.each2(function(i, choice) { - // hide an optgroup if it doesnt have any selectable children - if (!choice.is('.select2-result-selectable') - && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) { - choice.addClass("select2-selected"); + compound.each2(function(i, e) { + if (e.find(".select2-result-selectable").length==0) { + e.addClass("select2-disabled"); + } else { + e.removeClass("select2-disabled"); } }); - if (this.highlight() == -1 && noHighlightUpdate !== false){ - self.highlight(0); - } - - //If all results are chosen render formatNoMAtches - if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){ - if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) { - if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) { - this.results.append("
  • " + self.opts.formatNoMatches(self.search.val()) + "
  • "); - } + choices.each2(function (i, choice) { + if (!choice.hasClass("select2-disabled") && choice.hasClass("select2-result-selectable")) { + self.highlight(0); + return false; } - } - - }, + }); - // multi - getMaxSearchWidth: function() { - return this.selection.width() - getSideBorderPadding(this.search); }, // multi resizeSearch: function () { + var minimumWidth, left, maxWidth, containerLeft, searchWidth, - sideBorderPadding = getSideBorderPadding(this.search); + sideBorderPadding = getSideBorderPadding(this.search); minimumWidth = measureTextWidth(this.search) + 10; @@ -2952,7 +2175,6 @@ the specific language governing permissions and limitations under the Apache Lic containerLeft = this.selection.offset().left; searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; - if (searchWidth < minimumWidth) { searchWidth = maxWidth - sideBorderPadding; } @@ -2960,12 +2182,7 @@ the specific language governing permissions and limitations under the Apache Lic if (searchWidth < 40) { searchWidth = maxWidth - sideBorderPadding; } - - if (searchWidth <= 0) { - searchWidth = minimumWidth; - } - - this.search.width(Math.floor(searchWidth)); + this.search.width(searchWidth); }, // multi @@ -2996,47 +2213,19 @@ the specific language governing permissions and limitations under the Apache Lic }, // multi - buildChangeDetails: function (old, current) { - var current = current.slice(0), - old = old.slice(0); - - // remove intersection from each array - for (var i = 0; i < current.length; i++) { - for (var j = 0; j < old.length; j++) { - if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) { - current.splice(i, 1); - if(i>0){ - i--; - } - old.splice(j, 1); - j--; - } - } - } - - return {added: current, removed: old}; - }, - - - // multi - val: function (val, triggerChange) { - var oldData, self=this; + val: function () { + var val, data = [], self=this; if (arguments.length === 0) { return this.getVal(); } - oldData=this.data(); - if (!oldData.length) oldData=[]; + val = arguments[0]; - // val is an id. !val is true for [undefined,null,'',0] - 0 is legal - if (!val && val !== 0) { + if (!val) { this.opts.element.val(""); this.updateSelection([]); this.clearSearch(); - if (triggerChange) { - this.triggerChange({added: this.data(), removed: oldData}); - } return; } @@ -3044,23 +2233,20 @@ the specific language governing permissions and limitations under the Apache Lic this.setVal(val); if (this.select) { - this.opts.initSelection(this.select, this.bind(this.updateSelection)); - if (triggerChange) { - this.triggerChange(this.buildChangeDetails(oldData, this.data())); - } + this.select.find(":selected").each(function () { + data.push({id: $(this).attr("value"), text: $(this).text()}); + }); + this.updateSelection(data); } else { if (this.opts.initSelection === undefined) { - throw new Error("val() cannot be called if initSelection() is not defined"); + throw new Error("val() cannot be called if initSelection() is not defined") } this.opts.initSelection(this.opts.element, function(data){ - var ids=$.map(data, self.id); + var ids=$(data).map(self.id); self.setVal(ids); self.updateSelection(data); self.clearSearch(); - if (triggerChange) { - self.triggerChange(self.buildChangeDetails(oldData, self.data())); - } }); } this.clearSearch(); @@ -3091,6 +2277,7 @@ the specific language governing permissions and limitations under the Apache Lic this.resizeSearch(); // update selection + this.selection.find(".select2-search-choice").each(function() { val.push(self.opts.id($(this).data("select2-data"))); }); @@ -3099,23 +2286,19 @@ the specific language governing permissions and limitations under the Apache Lic }, // multi - data: function(values, triggerChange) { - var self=this, ids, old; + data: function(values) { + var self=this, ids; if (arguments.length === 0) { return this.selection .find(".select2-search-choice") .map(function() { return $(this).data("select2-data"); }) .get(); } else { - old = this.data(); if (!values) { values = []; } - ids = $.map(values, function(e) { return self.opts.id(e); }); + ids = $.map(values, function(e) { return self.opts.id(e)}); this.setVal(ids); this.updateSelection(values); this.clearSearch(); - if (triggerChange) { - this.triggerChange(this.buildChangeDetails(old, this.data())); - } } } }); @@ -3125,11 +2308,7 @@ the specific language governing permissions and limitations under the Apache Lic var args = Array.prototype.slice.call(arguments, 0), opts, select2, - method, value, multiple, - allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"], - valueMethods = ["opened", "isFocused", "container", "dropdown"], - propertyMethods = ["val", "data"], - methodsMap = { search: "externalSearch" }; + value, multiple, allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"]; this.each(function () { if (args.length === 0 || typeof(args[0]) === "object") { @@ -3137,7 +2316,7 @@ the specific language governing permissions and limitations under the Apache Lic opts.element = $(this); if (opts.element.get(0).tagName.toLowerCase() === "select") { - multiple = opts.element.prop("multiple"); + multiple = opts.element.attr("multiple"); } else { multiple = opts.multiple || false; if ("tags" in opts) {opts.multiple = multiple = true;} @@ -3154,22 +2333,12 @@ the specific language governing permissions and limitations under the Apache Lic value = undefined; select2 = $(this).data("select2"); if (select2 === undefined) return; - - method=args[0]; - - if (method === "container") { - value = select2.container; - } else if (method === "dropdown") { - value = select2.dropdown; + if (args[0] === "container") { + value=select2.container; } else { - if (methodsMap[method]) method = methodsMap[method]; - - value = select2[method].apply(select2, args.slice(1)); - } - if (indexOf(args[0], valueMethods) >= 0 - || (indexOf(args[0], propertyMethods) && args.length == 1)) { - return false; // abort the iteration, ready to return first matched value + value = select2[args[0]].apply(select2, args.slice(1)); } + if (value !== undefined) {return false;} } else { throw "Invalid arguments to select2 plugin: " + args; } @@ -3180,58 +2349,43 @@ the specific language governing permissions and limitations under the Apache Lic // plugin defaults, accessible to users $.fn.select2.defaults = { width: "copy", - loadMorePadding: 0, closeOnSelect: true, openOnEnter: true, containerCss: {}, dropdownCss: {}, containerCssClass: "", dropdownCssClass: "", - formatResult: function(result, container, query, escapeMarkup) { + formatResult: function(result, container, query) { var markup=[]; - markMatch(result.text, query.term, markup, escapeMarkup); + markMatch(result.text, query.term, markup); return markup.join(""); }, - formatSelection: function (data, container, escapeMarkup) { - return data ? escapeMarkup(data.text) : undefined; - }, - sortResults: function (results, container, query) { - return results; + formatSelection: function (data, container) { + return data.text; }, formatResultCssClass: function(data) {return undefined;}, - formatSelectionCssClass: function(data, container) {return undefined;}, formatNoMatches: function () { return "No matches found"; }, - formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " more character" + (n == 1? "" : "s"); }, - formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1? "" : "s"); }, + formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; }, formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, formatLoadMore: function (pageNumber) { return "Loading more results..."; }, formatSearching: function () { return "Searching..."; }, minimumResultsForSearch: 0, minimumInputLength: 0, - maximumInputLength: null, maximumSelectionSize: 0, id: function (e) { return e.id; }, matcher: function(term, text) { - return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0; + return text.toUpperCase().indexOf(term.toUpperCase()) >= 0; }, separator: ",", tokenSeparators: [], tokenizer: defaultTokenizer, - escapeMarkup: defaultEscapeMarkup, - blurOnChange: false, - selectOnBlur: false, - adaptContainerCssClass: function(c) { return c; }, - adaptDropdownCssClass: function(c) { return null; }, - nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; } - }; - - $.fn.select2.ajaxDefaults = { - transport: $.ajax, - params: { - type: "GET", - cache: false, - dataType: "json" - } + escapeMarkup: function (markup) { + if (markup && typeof(markup) === "string") { + return markup.replace(/&/g, "&"); + } + return markup; + }, + blurOnChange: false }; // exports @@ -3242,9 +2396,7 @@ the specific language governing permissions and limitations under the Apache Lic tags: tags }, util: { debounce: debounce, - markMatch: markMatch, - escapeMarkup: defaultEscapeMarkup, - stripDiacritics: stripDiacritics + markMatch: markMatch }, "class": { "abstract": AbstractSelect2, "single": SingleSelect2, diff --git a/addons/base_import/static/lib/select2/spinner.gif b/addons/base_import/static/lib/select2/spinner.gif new file mode 100644 index 0000000000000..5b33f7e54f4e5 Binary files /dev/null and b/addons/base_import/static/lib/select2/spinner.gif differ diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 987f30d962da2..3c029335fb162 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -51,12 +51,7 @@ openerp.base_import = function (instance) { type: 'ir.actions.client', tag: 'import', params: { - model: self.dataset.model, - // self.dataset.get_context() could be a compound? - // not sure. action's context should be evaluated - // so safer bet. Odd that timezone & al in it - // though - context: self.getParent().action.context, + model: self.dataset.model } }, { on_reverse_breadcrumb: function () { @@ -132,7 +127,6 @@ openerp.base_import = function (instance) { var self = this; this._super.apply(this, arguments); this.res_model = action.params.model; - this.parent_context = action.params.context || {}; // import object id this.id = null; this.Import = new instance.web.Model('base_import.import'); @@ -359,12 +353,11 @@ openerp.base_import = function (instance) { }, //- import itself - call_import: function (kwargs) { + call_import: function (options) { var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) { return $(el).select2('val') || false; }).get(); - kwargs.context = this.parent_context; - return this.Import.call('do', [this.id, fields, this.import_options()], kwargs) + return this.Import.call('do', [this.id, fields, this.import_options()], options) .then(undefined, function (error, event) { // In case of unexpected exception, convert // "JSON-RPC error" to an import failure, and @@ -482,13 +475,5 @@ openerp.base_import = function (instance) { { name: 'import_succeeded', from: 'importing', to: 'imported'}, { name: 'import_failed', from: 'importing', to: 'results' } ] - }); - - $.extend($.fn.select2.defaults, { - formatNoMatches: function () { return _t("No matches found"); }, - formatLoadMore: function (pageNumber) { return _t("Loading more results..."); }, - formatSearching: function () { return _t("Searching..."); } - }); - + }) }; - diff --git a/addons/base_import/test_models.py b/addons/base_import/test_models.py index 0720ae11eda25..b951cf80c0597 100644 --- a/addons/base_import/test_models.py +++ b/addons/base_import/test_models.py @@ -1,101 +1,94 @@ -from openerp.osv import orm, fields - -def name(n): return 'base_import.tests.models.%s' % n - -class char(orm.Model): - _name = name('char') - - _columns = { - 'value': fields.char('unknown', size=None) - } - -class char_required(orm.Model): - _name = name('char.required') - - _columns = { - 'value': fields.char('unknown', size=None, required=True) - } - -class char_readonly(orm.Model): - _name = name('char.readonly') - - _columns = { - 'value': fields.char('unknown', size=None, readonly=True) - } - -class char_states(orm.Model): - _name = name('char.states') - - _columns = { - 'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', False)]}) - } - -class char_noreadonly(orm.Model): - _name = name('char.noreadonly') - - _columns = { - 'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('invisible', True)]}) - } - -class char_stillreadonly(orm.Model): - _name = name('char.stillreadonly') - - _columns = { - 'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', True)]}) - } - -# TODO: complex field (m2m, o2m, m2o) -class m2o(orm.Model): - _name = name('m2o') - - _columns = { - 'value': fields.many2one(name('m2o.related')) - } -class m2o_related(orm.Model): - _name = name('m2o.related') - - _columns = { - 'value': fields.integer() - } - _defaults = { - 'value': 42 - } - -class m2o_required(orm.Model): - _name = name('m2o.required') - - _columns = { - 'value': fields.many2one(name('m2o.required.related'), required=True) - } -class m2o_required_related(orm.Model): - _name = name('m2o.required.related') - - _columns = { - 'value': fields.integer() - } - _defaults = { - 'value': 42 - } - -class o2m(orm.Model): - _name = name('o2m') - - _columns = { - 'value': fields.one2many(name('o2m.child'), 'parent_id') - } -class o2m_child(orm.Model): - _name = name('o2m.child') - - _columns = { - 'parent_id': fields.many2one(name('o2m')), - 'value': fields.integer() - } - -class preview_model(orm.Model): - _name = name('preview') - - _columns = { - 'name': fields.char('Name', size=None), - 'somevalue': fields.integer('Some Value', required=True), - 'othervalue': fields.integer('Other Variable'), - } +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import orm, fields + +def name(n): + return 'base_import.tests.models.%s' % n + + +class char(orm.Model): + _name = name('char') + _columns = {'value': fields.char('unknown', size=None)} + + +class char_required(orm.Model): + _name = name('char.required') + _columns = {'value': fields.char('unknown', size=None, required=True)} + + +class char_readonly(orm.Model): + _name = name('char.readonly') + _columns = {'value': fields.char('unknown', size=None, readonly=True)} + + +class char_states(orm.Model): + _name = name('char.states') + _columns = {'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', False)]})} + + +class char_noreadonly(orm.Model): + _name = name('char.noreadonly') + _columns = {'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('invisible', True)]})} + + +class char_stillreadonly(orm.Model): + _name = name('char.stillreadonly') + _columns = {'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', True)]})} + + +class m2o(orm.Model): + _name = name('m2o') + _columns = {'value': fields.many2one(name('m2o.related'))} + + +class m2o_related(orm.Model): + _name = name('m2o.related') + _columns = {'value': fields.integer()} + _defaults = {'value': 42} + + +class m2o_required(orm.Model): + _name = name('m2o.required') + _columns = {'value': fields.many2one(name('m2o.required.related'), required=True)} + + +class m2o_required_related(orm.Model): + _name = name('m2o.required.related') + _columns = {'value': fields.integer()} + _defaults = {'value': 42} + + +class o2m(orm.Model): + _name = name('o2m') + _columns = {'value': fields.one2many(name('o2m.child'), 'parent_id')} + + +class o2m_child(orm.Model): + _name = name('o2m.child') + _columns = {'parent_id': fields.many2one(name('o2m')), + 'value': fields.integer()} + + +class preview_model(orm.Model): + _name = name('preview') + _columns = {'name': fields.char('Name', size=None), + 'somevalue': fields.integer('Some Value', required=True), + 'othervalue': fields.integer('Other Variable')} \ No newline at end of file diff --git a/addons/base_report_designer/base_report_designer.py b/addons/base_report_designer/base_report_designer.py index 180a534b07f7b..5bdc3108713c7 100644 --- a/addons/base_report_designer/base_report_designer.py +++ b/addons/base_report_designer/base_report_designer.py @@ -1,88 +1,76 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import base64 -import openerp.modules.registry -from openerp.osv import osv -from openerp_sxw2rml import sxw2rml -from StringIO import StringIO -from openerp import pooler -from openerp import addons - - -class report_xml(osv.osv): - _inherit = 'ir.actions.report.xml' - - def sxwtorml(self, cr, uid, file_sxw, file_type): - ''' - The use of this function is to get rml file from sxw file. - ''' - sxwval = StringIO(base64.decodestring(file_sxw)) - if file_type=='sxw': - fp = open(addons.get_module_resource('base_report_designer','openerp_sxw2rml', 'normalized_oo2rml.xsl'),'rb') - if file_type=='odt': - fp = open(addons.get_module_resource('base_report_designer','openerp_sxw2rml', 'normalized_odt2rml.xsl'),'rb') - return {'report_rml_content': str(sxw2rml(sxwval, xsl=fp.read()))} - - def upload_report(self, cr, uid, report_id, file_sxw, file_type, context=None): - ''' - Untested function - ''' - pool = pooler.get_pool(cr.dbname) - sxwval = StringIO(base64.decodestring(file_sxw)) - if file_type=='sxw': - fp = open(addons.get_module_resource('base_report_designer','openerp_sxw2rml', 'normalized_oo2rml.xsl'),'rb') - if file_type=='odt': - fp = open(addons.get_module_resource('base_report_designer','openerp_sxw2rml', 'normalized_odt2rml.xsl'),'rb') - report = pool.get('ir.actions.report.xml').write(cr, uid, [report_id], { - 'report_sxw_content': base64.decodestring(file_sxw), - 'report_rml_content': str(sxw2rml(sxwval, xsl=fp.read())), - }) - - # FIXME: this should be moved to an override of the ir.actions.report_xml.create() method - cr.commit() - pool.get('ir.actions.report.xml').register_all(cr) - openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname) - - return True - - def report_get(self, cr, uid, report_id, context=None): - if context is None: - context = {} - # skip osv.fields.sanitize_binary_value() because we want the raw bytes in all cases - context.update(bin_raw=True) - report = self.browse(cr, uid, report_id, context=context) - sxw_data = report.report_sxw_content - rml_data = report.report_rml_content - if isinstance(sxw_data, unicode): - sxw_data = sxw_data.encode("iso-8859-1", "replace") - if isinstance(rml_data, unicode): - rml_data = rml_data.encode("iso-8859-1", "replace") - return { - 'file_type' : report.report_type, - 'report_sxw_content': sxw_data and base64.encodestring(sxw_data) or False, - 'report_rml_content': rml_data and base64.encodestring(rml_data) or False - } - -report_xml() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import base64 +import openerp.modules.registry +from openerp.osv import osv +from openerp_sxw2rml import sxw2rml +from StringIO import StringIO +from openerp import pooler +from openerp import addons + +class report_xml(osv.osv): + _inherit = 'ir.actions.report.xml' + + def sxwtorml(self, cr, uid, file_sxw, file_type): + """ + The use of this function is to get rml file from sxw file. + """ + sxwval = StringIO(base64.decodestring(file_sxw)) + if file_type == 'sxw': + fp = open(addons.get_module_resource('base_report_designer', 'openerp_sxw2rml', 'normalized_oo2rml.xsl'), 'rb') + if file_type == 'odt': + fp = open(addons.get_module_resource('base_report_designer', 'openerp_sxw2rml', 'normalized_odt2rml.xsl'), 'rb') + return {'report_rml_content': str(sxw2rml(sxwval, xsl=fp.read()))} + + def upload_report(self, cr, uid, report_id, file_sxw, file_type, context = None): + """ + Untested function + """ + pool = pooler.get_pool(cr.dbname) + sxwval = StringIO(base64.decodestring(file_sxw)) + if file_type == 'sxw': + fp = open(addons.get_module_resource('base_report_designer', 'openerp_sxw2rml', 'normalized_oo2rml.xsl'), 'rb') + if file_type == 'odt': + fp = open(addons.get_module_resource('base_report_designer', 'openerp_sxw2rml', 'normalized_odt2rml.xsl'), 'rb') + report = pool.get('ir.actions.report.xml').write(cr, uid, [report_id], {'report_sxw_content': base64.decodestring(file_sxw), + 'report_rml_content': str(sxw2rml(sxwval, xsl=fp.read()))}) + cr.commit() + pool.get('ir.actions.report.xml').register_all(cr) + openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname) + return True + + def report_get(self, cr, uid, report_id, context = None): + if context is None: + context = {} + context.update(bin_raw=True) + report = self.browse(cr, uid, report_id, context=context) + sxw_data = report.report_sxw_content + rml_data = report.report_rml_content + if isinstance(sxw_data, unicode): + sxw_data = sxw_data.encode('iso-8859-1', 'replace') + if isinstance(rml_data, unicode): + rml_data = rml_data.encode('iso-8859-1', 'replace') + return {'file_type': report.report_type, + 'report_sxw_content': sxw_data and base64.encodestring(sxw_data) or False, + 'report_rml_content': rml_data and base64.encodestring(rml_data) or False} + + +report_xml() \ No newline at end of file diff --git a/addons/base_report_designer/images/base_report_designer1.jpeg b/addons/base_report_designer/images/base_report_designer1.jpeg new file mode 100644 index 0000000000000..4d3e71eebe644 Binary files /dev/null and b/addons/base_report_designer/images/base_report_designer1.jpeg differ diff --git a/addons/base_report_designer/images/base_report_designer2.jpeg b/addons/base_report_designer/images/base_report_designer2.jpeg new file mode 100644 index 0000000000000..99a9bced852cf Binary files /dev/null and b/addons/base_report_designer/images/base_report_designer2.jpeg differ diff --git a/addons/base_report_designer/installer.py b/addons/base_report_designer/installer.py index bfda6e7a30f3f..346b7e0fe954b 100644 --- a/addons/base_report_designer/installer.py +++ b/addons/base_report_designer/installer.py @@ -1,63 +1,44 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields -from openerp.osv import osv -import base64 -from openerp.tools.translate import _ -from openerp import addons - -class base_report_designer_installer(osv.osv_memory): - _name = 'base_report_designer.installer' - _inherit = 'res.config.installer' - - def default_get(self, cr, uid, fields, context=None): - data = super(base_report_designer_installer, self).default_get(cr, uid, fields, context=context) - plugin_file = open(addons.get_module_resource('base_report_designer','plugin', 'openerp_report_designer.zip'),'rb') - data['plugin_file'] = base64.encodestring(plugin_file.read()) - return data - - _columns = { - 'name':fields.char('File name', size=34), - 'plugin_file':fields.binary('OpenObject Report Designer Plug-in', readonly=True, help="OpenObject Report Designer plug-in file. Save as this file and install this plug-in in OpenOffice."), - 'description':fields.text('Description', readonly=True) - } - - _defaults = { - 'name' : 'openerp_report_designer.zip', - 'description' : """ - * Save the OpenERP Report Designer plug-­in. - * Follow these steps to install plug-­in. - 1. Open Extension Manager window from Menu Bar of Openoffice writer, Open Tools > Extension Menu. - 2. Click on "Add" button. - 3. Select path where the openerp_report_designer.zip is located. - 4. On the completion of adding package you will get your package under 'Extension Manager' and the status of your package become 'Enabled'. - 5. Restart openoffice writer. - * Follow the steps to configure OpenERP Report Designer plug-­in in Openoffice writer. - 1. Connect OpenERP Server from Menu bar , OpenERP Report Designer > Server parameter. - 2. Select Server url, database and provide user name and password - 3. Click "Connect". - 4. if your connection success, A message appears like 'You can start creating your report in current document.'. - """ - } -base_report_designer_installer() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields +from openerp.osv import osv +import base64 +from openerp.tools.translate import _ +from openerp import addons + +class base_report_designer_installer(osv.osv_memory): + _name = 'base_report_designer.installer' + _inherit = 'res.config.installer' + + def default_get(self, cr, uid, fields, context = None): + data = super(base_report_designer_installer, self).default_get(cr, uid, fields, context=context) + plugin_file = open(addons.get_module_resource('base_report_designer', 'plugin', 'openerp_report_designer.zip'), 'rb') + data['plugin_file'] = base64.encodestring(plugin_file.read()) + return data + + _columns = {'name': fields.char('File name', size=34), + 'plugin_file': fields.binary('OpenObject Report Designer Plug-in', readonly=True, help='OpenObject Report Designer plug-in file. Save as this file and install this plug-in in OpenOffice.'), + 'description': fields.text('Description', readonly=True)} + _defaults = {'name': 'openerp_report_designer.zip', + 'description': '\n * Save the OpenERP Report Designer plug-\xc2\xadin.\n * Follow these steps to install plug-\xc2\xadin.\n 1. Open Extension Manager window from Menu Bar of Openoffice writer, Open Tools > Extension Menu.\n 2. Click on "Add" button.\n 3. Select path where the openerp_report_designer.zip is located.\n 4. On the completion of adding package you will get your package under \'Extension Manager\' and the status of your package become \'Enabled\'.\n 5. Restart openoffice writer.\n * Follow the steps to configure OpenERP Report Designer plug-\xc2\xadin in Openoffice writer.\n 1. Connect OpenERP Server from Menu bar , OpenERP Report Designer > Server parameter.\n 2. Select Server url, database and provide user name and password\n 3. Click "Connect".\n 4. if your connection success, A message appears like \'You can start creating your report in current document.\'.\n '} + + +base_report_designer_installer() \ No newline at end of file diff --git a/addons/base_report_designer/openerp_sxw2rml/openerp_sxw2rml.py b/addons/base_report_designer/openerp_sxw2rml/openerp_sxw2rml.py old mode 100755 new mode 100644 index 436650eb2cd4e..7a9633bc60c44 --- a/addons/base_report_designer/openerp_sxw2rml/openerp_sxw2rml.py +++ b/addons/base_report_designer/openerp_sxw2rml/openerp_sxw2rml.py @@ -1,362 +1,347 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (c): -# -# 2005 pyopenoffice.py Martin Simon (http://www.bezirksreiter.de) -# 2005 Fabien Pinckaers, TINY SPRL. (http://tiny.be) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -#!/usr/bin/python -""" -OpenERP SXW2RML - The OpenERP's report engine - -OpenERP SXW2RML is part of the OpenERP Report Project. -OpenERP Report is a module that allows you to render high quality PDF document -from an OpenOffice template (.sxw) and any relationl database. -""" -__version__ = '0.9' - - -import re -import string -import os -import zipfile -import xml.dom.minidom -from reportlab.lib.units import toLength -import base64 -import copy - -class DomApiGeneral: - """General DOM API utilities.""" - def __init__(self, content_string="", file=""): - self.content_string = content_string - self.re_digits = re.compile(r"(.*?\d)(pt|cm|mm|inch|in)") - - def _unitTuple(self, string): - """Split values and units to a tuple.""" - temp = self.re_digits.findall(string) - if not temp: - return (string,"") - else: - return (temp[0]) - - def stringPercentToFloat(self, string): - temp = string.replace("""%""","") - return float(temp)/100 - - def findChildrenByName(self, parent, name, attr_dict=None): - """Helper functions. Does not work recursively. - Optional: also test for certain attribute/value pairs.""" - if attr_dict is None: - attr_dict = {} - children = [] - for c in parent.childNodes: - if c.nodeType == c.ELEMENT_NODE and c.nodeName == name: - children.append(c) - if attr_dict == {}: - return children - else: - return self._selectForAttributes(nodelist=children,attr_dict=attr_dict) - - def _selectForAttributes(self, nodelist, attr_dict): - "Helper function.""" - selected_nodes = [] - for n in nodelist: - check = 1 - for a in attr_dict.keys(): - if n.getAttribute(a) != attr_dict[a]: - # at least one incorrect attribute value? - check = 0 - if check: - selected_nodes.append(n) - return selected_nodes - - def _stringToTuple(self, s): - """Helper function.""" - try: - temp = string.split(s,",") - return int(temp[0]),int(temp[1]) - except: - return None - - def _tupleToString(self, t): - try: - return self.openOfficeStringUtf8("%s,%s" % (t[0],t[1])) - except: - return None - - def _lengthToFloat(self, value): - v = value - if not self.re_digits.search(v): - return v - try: - if v[-4:] == "inch": - # OO files use "inch" instead of "in" in Reportlab units - v = v[:-2] - except: - pass - try: - c = round(toLength(v)) - return c - except: - return v - - def openOfficeStringUtf8(self, string): - if type(string) == unicode: - return string.encode("utf-8") - tempstring = unicode(string,"cp1252").encode("utf-8") - return tempstring - -class DomApi(DomApiGeneral): - """This class provides a DOM-API for XML-Files from an SXW-Archive.""" - def __init__(self, xml_content, xml_styles): - DomApiGeneral.__init__(self) - self.content_dom = xml.dom.minidom.parseString(xml_content) - self.styles_dom = xml.dom.minidom.parseString(xml_styles) - body = self.content_dom.getElementsByTagName("office:body") - self.body = body and body[0] - - # TODO: - self.style_dict = {} - self.style_properties_dict = {} - - # ******** always use the following order: - self.buildStyleDict() - self.buildStylePropertiesDict() - if self.styles_dom.getElementsByTagName("style:page-master").__len__()<>0: - self.page_master = self.styles_dom.getElementsByTagName("style:page-master")[0] - if self.styles_dom.getElementsByTagName("style:page-layout").__len__()<>0 : - self.page_master = self.styles_dom.getElementsByTagName("style:page-layout")[0] - self.document = self.content_dom.getElementsByTagName("office:document-content")[0] - - def buildStylePropertiesDict(self): - for s in self.style_dict.keys(): - self.style_properties_dict[s] = self.getStylePropertiesDict(s) - - def updateWithPercents(self, dict, updatedict): - """Sometimes you find values like "115%" in the style hierarchy.""" - if not updatedict: - # no style hierarchies for this style? => - return - new_updatedict = copy.copy(updatedict) - for u in new_updatedict.keys(): - try: - if new_updatedict[u].find("""%""") != -1 and dict.has_key(u): - number = float(self.re_digits.search(dict[u]).group(1)) - unit = self.re_digits.search(dict[u]).group(2) - new_number = self.stringPercentToFloat(new_updatedict[u]) * number - if unit == "pt": - new_number = int(new_number) - # no floats allowed for "pt" - # OOo just takes the int, does not round (try it out!) - new_updatedict[u] = "%s%s" % (new_number,unit) - else: - dict[u] = new_updatedict[u] - except: - dict[u] = new_updatedict[u] - dict.update(new_updatedict) - - def normalizeStyleProperties(self): - """Transfer all style:style-properties attributes from the - self.style_properties_hierarchical dict to the automatic-styles - from content.xml. Use this function to preprocess content.xml for - XSLT transformations etc.Do not try to implement this function - with XSlT - believe me, it's a terrible task...""" - styles_styles = self.styles_dom.getElementsByTagName("style:style") - automatic_styles = self.content_dom.getElementsByTagName("office:automatic-styles")[0] - for s in styles_styles: - automatic_styles.appendChild(s.cloneNode(deep=1)) - content_styles = self.content_dom.getElementsByTagName("style:style") - # these are the content_styles with styles_styles added!!! - for s in content_styles: - c = self.findChildrenByName(s,"style:properties") - if c == []: - # some derived automatic styles do not have "style:properties": - temp = self.content_dom.createElement("style:properties") - s.appendChild(temp) - c = self.findChildrenByName(s,"style:properties") - c = c[0] - dict = self.style_properties_dict[(s.getAttribute("style:name")).encode("utf-8")] or {} - for attribute in dict.keys(): - c.setAttribute(self.openOfficeStringUtf8(attribute),self.openOfficeStringUtf8(dict[attribute])) - - def transferStylesXml(self): - """Transfer certain sub-trees from styles.xml to the normalized content.xml - (see above). It is not necessary to do this - for example - with paragraph styles. - the "normalized" style properties contain all information needed for - further processing.""" - # TODO: What about table styles etc.? - outline_styles = self.styles_dom.getElementsByTagName("text:outline-style") - t = self.content_dom.createElement("transferredfromstylesxml") - self.document.insertBefore(t,self.body) - t_new = self.body.previousSibling - try: - page_master = self.page_master - t_new.appendChild(page_master.cloneNode(deep=1)) - t_new.appendChild(outline_styles[0].cloneNode(deep=1)) - except: - pass - - def normalizeLength(self): - """Normalize all lengthes to floats (i.e: 1 inch = 72). - Always use this after "normalizeContent" and "transferStyles"!""" - # TODO: The complex attributes of table cell styles are not transferred yet. - #all_styles = self.content_dom.getElementsByTagName("style:properties") - #all_styles += self.content_dom.getElementsByTagName("draw:image") - all_styles = self.content_dom.getElementsByTagName("*") - for s in all_styles: - for x in s._attrs.keys(): - v = s.getAttribute(x) - s.setAttribute(x,"%s" % self._lengthToFloat(v)) - # convert float to string first! - - def normalizeTableColumns(self): - """Handle this strange table:number-columns-repeated attribute.""" - columns = self.content_dom.getElementsByTagName("table:table-column") - for c in columns: - if c.hasAttribute("table:number-columns-repeated"): - number = int(c.getAttribute("table:number-columns-repeated")) - c.removeAttribute("table:number-columns-repeated") - for i in range(number-1): - (c.parentNode).insertBefore(c.cloneNode(deep=1),c) - - def buildStyleDict(self): - """Store all style:style-nodes from content.xml and styles.xml in self.style_dict. - Caution: in this dict the nodes from two dom apis are merged!""" - for st in (self.styles_dom,self.content_dom): - for s in st.getElementsByTagName("style:style"): - name = s.getAttribute("style:name").encode("utf-8") - self.style_dict[name] = s - return True - - def toxml(self): - return self.content_dom.toxml(encoding="utf-8") - - def getStylePropertiesDict(self, style_name): - res = {} - - if self.style_dict[style_name].hasAttribute("style:parent-style-name"): - parent = self.style_dict[style_name].getAttribute("style:parent-style-name").encode("utf-8") - res = self.getStylePropertiesDict(parent) - - children = self.style_dict[style_name].childNodes - for c in children: - if c.nodeType == c.ELEMENT_NODE and c.nodeName.find("properties")>0 : - for attr in c._attrs.keys(): - res[attr] = c.getAttribute(attr).encode("utf-8") - return res - -class PyOpenOffice(object): - """This is the main class which provides all functionality.""" - def __init__(self, path='.', save_pict=False): - self.path = path - self.save_pict = save_pict - self.images = {} - - def oo_read(self, fname): - z = zipfile.ZipFile(fname,"r") - content = z.read('content.xml') - style = z.read('styles.xml') - all = z.namelist() - for a in all: - if a[:9]=='Pictures/' and len(a)>10: - pic_content = z.read(a) - self.images[a[9:]] = pic_content - if self.save_pict: - f=open(os.path.join(self.path, os.path.basename(a)),"wb") - f.write(pic_content) - f.close() - z.close() - return content,style - - def oo_replace(self, content): - regex = [ - (r"]*/>", ""), - (r"(.*?)]*/>", "$2"), - ] - for key,val in regex: - content = re.sub(key, val, content) - return content - - def unpackNormalize(self, sourcefile): - c,s = self.oo_read(sourcefile) - c = self.oo_replace(c) - dom = DomApi(c,s) - dom.normalizeStyleProperties() - dom.transferStylesXml() - dom.normalizeLength() - dom.normalizeTableColumns() - new_c = dom.toxml() - return new_c - -def sxw2rml(sxw_file, xsl, output='.', save_pict=False): - from lxml import etree - from StringIO import StringIO - - tool = PyOpenOffice(output, save_pict = save_pict) - res = tool.unpackNormalize(sxw_file) - - f = StringIO(xsl) - styledoc = etree.parse(f) - style = etree.XSLT(styledoc) - - f = StringIO(res) - doc = etree.parse(f) - result = style(doc) - root = etree.XPathEvaluator(result)("/document/stylesheet") - - if root: - root=root[0] - images = etree.Element("images") - for img in tool.images: - node = etree.Element('image', name=img) - node.text = base64.encodestring(tool.images[img]) - images.append(node) - root.append(images) - - try: - xml = str(result) - return xml - except: - return result - -if __name__ == "__main__": - import optparse - parser = optparse.OptionParser( - version="OpenERP Report v%s" % __version__, - usage = 'openerp_sxw2rml.py [options] file.sxw') - parser.add_option("-v", "--verbose", default=False, dest="verbose", help="enable basic debugging") - parser.add_option("-o", "--output", dest="output", default='.', help="directory of image output") - (opt, args) = parser.parse_args() - if len(args) != 1: - parser.error("Incorrect number of arguments.") - - import sys - - fname = sys.argv[1] - f = fname - xsl_file = 'normalized_oo2rml.xsl' - z = zipfile.ZipFile(fname,"r") - mimetype = z.read('mimetype') - if mimetype.split('/')[-1] == 'vnd.oasis.opendocument.text' : - xsl_file = 'normalized_odt2rml.xsl' - xsl = file(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), xsl_file)).read() - result = sxw2rml(f, xsl, output=opt.output, save_pict=False) - - print result -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +""" +OpenERP SXW2RML - The OpenERP's report engine + +OpenERP SXW2RML is part of the OpenERP Report Project. +OpenERP Report is a module that allows you to render high quality PDF document +from an OpenOffice template (.sxw) and any relationl database. +""" +__version__ = '0.9' +import re +import string +import os +import zipfile +import xml.dom.minidom +from reportlab.lib.units import toLength +import base64 +import copy + +class DomApiGeneral: + """General DOM API utilities.""" + + def __init__(self, content_string = '', file = ''): + self.content_string = content_string + self.re_digits = re.compile('(.*?\\d)(pt|cm|mm|inch|in)') + + def _unitTuple(self, string): + """Split values and units to a tuple.""" + temp = self.re_digits.findall(string) + if not temp: + return (string, '') + else: + return temp[0] + + def stringPercentToFloat(self, string): + temp = string.replace('%', '') + return float(temp) / 100 + + def findChildrenByName(self, parent, name, attr_dict = None): + """Helper functions. Does not work recursively. + Optional: also test for certain attribute/value pairs.""" + if attr_dict is None: + attr_dict = {} + children = [] + for c in parent.childNodes: + if c.nodeType == c.ELEMENT_NODE and c.nodeName == name: + children.append(c) + + if attr_dict == {}: + return children + else: + return self._selectForAttributes(nodelist=children, attr_dict=attr_dict) + return + + def _selectForAttributes(self, nodelist, attr_dict): + """Helper function.""" + selected_nodes = [] + for n in nodelist: + check = 1 + for a in attr_dict.keys(): + if n.getAttribute(a) != attr_dict[a]: + check = 0 + + if check: + selected_nodes.append(n) + + return selected_nodes + + def _stringToTuple(self, s): + """Helper function.""" + try: + temp = string.split(s, ',') + return (int(temp[0]), int(temp[1])) + except: + return None + + return None + + def _tupleToString(self, t): + try: + return self.openOfficeStringUtf8('%s,%s' % (t[0], t[1])) + except: + return None + + return None + + def _lengthToFloat(self, value): + v = value + if not self.re_digits.search(v): + return v + try: + if v[-4:] == 'inch': + v = v[:-2] + except: + pass + + try: + c = round(toLength(v)) + return c + except: + return v + + def openOfficeStringUtf8(self, string): + if type(string) == unicode: + return string.encode('utf-8') + tempstring = unicode(string, 'cp1252').encode('utf-8') + return tempstring + + +class DomApi(DomApiGeneral): + """This class provides a DOM-API for XML-Files from an SXW-Archive.""" + + def __init__(self, xml_content, xml_styles): + DomApiGeneral.__init__(self) + self.content_dom = xml.dom.minidom.parseString(xml_content) + self.styles_dom = xml.dom.minidom.parseString(xml_styles) + body = self.content_dom.getElementsByTagName('office:body') + self.body = body and body[0] + self.style_dict = {} + self.style_properties_dict = {} + self.buildStyleDict() + self.buildStylePropertiesDict() + if self.styles_dom.getElementsByTagName('style:page-master').__len__() != 0: + self.page_master = self.styles_dom.getElementsByTagName('style:page-master')[0] + if self.styles_dom.getElementsByTagName('style:page-layout').__len__() != 0: + self.page_master = self.styles_dom.getElementsByTagName('style:page-layout')[0] + self.document = self.content_dom.getElementsByTagName('office:document-content')[0] + + def buildStylePropertiesDict(self): + for s in self.style_dict.keys(): + self.style_properties_dict[s] = self.getStylePropertiesDict(s) + + def updateWithPercents(self, dict, updatedict): + """Sometimes you find values like "115%" in the style hierarchy.""" + if not updatedict: + return + new_updatedict = copy.copy(updatedict) + for u in new_updatedict.keys(): + try: + if new_updatedict[u].find('%') != -1 and dict.has_key(u): + number = float(self.re_digits.search(dict[u]).group(1)) + unit = self.re_digits.search(dict[u]).group(2) + new_number = self.stringPercentToFloat(new_updatedict[u]) * number + if unit == 'pt': + new_number = int(new_number) + new_updatedict[u] = '%s%s' % (new_number, unit) + else: + dict[u] = new_updatedict[u] + except: + dict[u] = new_updatedict[u] + + dict.update(new_updatedict) + + def normalizeStyleProperties(self): + """Transfer all style:style-properties attributes from the + self.style_properties_hierarchical dict to the automatic-styles + from content.xml. Use this function to preprocess content.xml for + XSLT transformations etc.Do not try to implement this function + with XSlT - believe me, it's a terrible task...""" + styles_styles = self.styles_dom.getElementsByTagName('style:style') + automatic_styles = self.content_dom.getElementsByTagName('office:automatic-styles')[0] + for s in styles_styles: + automatic_styles.appendChild(s.cloneNode(deep=1)) + + content_styles = self.content_dom.getElementsByTagName('style:style') + for s in content_styles: + c = self.findChildrenByName(s, 'style:properties') + if c == []: + temp = self.content_dom.createElement('style:properties') + s.appendChild(temp) + c = self.findChildrenByName(s, 'style:properties') + c = c[0] + dict = self.style_properties_dict[s.getAttribute('style:name').encode('utf-8')] or {} + for attribute in dict.keys(): + c.setAttribute(self.openOfficeStringUtf8(attribute), self.openOfficeStringUtf8(dict[attribute])) + + def transferStylesXml(self): + """Transfer certain sub-trees from styles.xml to the normalized content.xml + (see above). It is not necessary to do this - for example - with paragraph styles. + the "normalized" style properties contain all information needed for + further processing.""" + outline_styles = self.styles_dom.getElementsByTagName('text:outline-style') + t = self.content_dom.createElement('transferredfromstylesxml') + self.document.insertBefore(t, self.body) + t_new = self.body.previousSibling + try: + page_master = self.page_master + t_new.appendChild(page_master.cloneNode(deep=1)) + t_new.appendChild(outline_styles[0].cloneNode(deep=1)) + except: + pass + + def normalizeLength(self): + """Normalize all lengthes to floats (i.e: 1 inch = 72). + Always use this after "normalizeContent" and "transferStyles"!""" + all_styles = self.content_dom.getElementsByTagName('*') + for s in all_styles: + for x in s._attrs.keys(): + v = s.getAttribute(x) + s.setAttribute(x, '%s' % self._lengthToFloat(v)) + + def normalizeTableColumns(self): + """Handle this strange table:number-columns-repeated attribute.""" + columns = self.content_dom.getElementsByTagName('table:table-column') + for c in columns: + if c.hasAttribute('table:number-columns-repeated'): + number = int(c.getAttribute('table:number-columns-repeated')) + c.removeAttribute('table:number-columns-repeated') + for i in range(number - 1): + c.parentNode.insertBefore(c.cloneNode(deep=1), c) + + def buildStyleDict(self): + """Store all style:style-nodes from content.xml and styles.xml in self.style_dict. + Caution: in this dict the nodes from two dom apis are merged!""" + for st in (self.styles_dom, self.content_dom): + for s in st.getElementsByTagName('style:style'): + name = s.getAttribute('style:name').encode('utf-8') + self.style_dict[name] = s + + return True + + def toxml(self): + return self.content_dom.toxml(encoding='utf-8') + + def getStylePropertiesDict(self, style_name): + res = {} + if self.style_dict[style_name].hasAttribute('style:parent-style-name'): + parent = self.style_dict[style_name].getAttribute('style:parent-style-name').encode('utf-8') + res = self.getStylePropertiesDict(parent) + children = self.style_dict[style_name].childNodes + for c in children: + if c.nodeType == c.ELEMENT_NODE and c.nodeName.find('properties') > 0: + for attr in c._attrs.keys(): + res[attr] = c.getAttribute(attr).encode('utf-8') + + return res + + +class PyOpenOffice(object): + """This is the main class which provides all functionality.""" + + def __init__(self, path = '.', save_pict = False): + self.path = path + self.save_pict = save_pict + self.images = {} + + def oo_read(self, fname): + z = zipfile.ZipFile(fname, 'r') + content = z.read('content.xml') + style = z.read('styles.xml') + all = z.namelist() + for a in all: + if a[:9] == 'Pictures/' and len(a) > 10: + pic_content = z.read(a) + self.images[a[9:]] = pic_content + if self.save_pict: + f = open(os.path.join(self.path, os.path.basename(a)), 'wb') + f.write(pic_content) + f.close() + + z.close() + return (content, style) + + def oo_replace(self, content): + regex = [(']*/>', ''), ('(.*?)]*/>', '$2')] + for key, val in regex: + content = re.sub(key, val, content) + + return content + + def unpackNormalize(self, sourcefile): + c, s = self.oo_read(sourcefile) + c = self.oo_replace(c) + dom = DomApi(c, s) + dom.normalizeStyleProperties() + dom.transferStylesXml() + dom.normalizeLength() + dom.normalizeTableColumns() + new_c = dom.toxml() + return new_c + + +def sxw2rml(sxw_file, xsl, output = '.', save_pict = False): + from lxml import etree + from StringIO import StringIO + tool = PyOpenOffice(output, save_pict=save_pict) + res = tool.unpackNormalize(sxw_file) + f = StringIO(xsl) + styledoc = etree.parse(f) + style = etree.XSLT(styledoc) + f = StringIO(res) + doc = etree.parse(f) + result = style(doc) + root = etree.XPathEvaluator(result)('/document/stylesheet') + if root: + root = root[0] + images = etree.Element('images') + for img in tool.images: + node = etree.Element('image', name=img) + node.text = base64.encodestring(tool.images[img]) + images.append(node) + + root.append(images) + try: + xml = str(result) + return xml + except: + return result + + +if __name__ == '__main__': + import optparse + parser = optparse.OptionParser(version='OpenERP Report v%s' % __version__, usage='openerp_sxw2rml.py [options] file.sxw') + parser.add_option('-v', '--verbose', default=False, dest='verbose', help='enable basic debugging') + parser.add_option('-o', '--output', dest='output', default='.', help='directory of image output') + opt, args = parser.parse_args() + if len(args) != 1: + parser.error('Incorrect number of arguments.') + import sys + fname = sys.argv[1] + f = fname + xsl_file = 'normalized_oo2rml.xsl' + z = zipfile.ZipFile(fname, 'r') + mimetype = z.read('mimetype') + if mimetype.split('/')[-1] == 'vnd.oasis.opendocument.text': + xsl_file = 'normalized_odt2rml.xsl' + xsl = file(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), xsl_file)).read() + result = sxw2rml(f, xsl, output=opt.output, save_pict=False) + print result \ No newline at end of file diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/OOo_run.sh b/addons/base_report_designer/plugin/openerp_report_designer/bin/OOo_run.sh old mode 100755 new mode 100644 diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/About.jpg b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/About.jpg new file mode 100644 index 0000000000000..355c35f04ef75 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/About.jpg differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/aboutus_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/aboutus_b.png new file mode 100644 index 0000000000000..19a42e26b4fec Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/aboutus_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/aboutus_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/aboutus_s.png new file mode 100644 index 0000000000000..5b48356e69255 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/aboutus_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetBtoF_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetBtoF_b.png new file mode 100644 index 0000000000000..80c1a629bff5a Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetBtoF_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetBtoF_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetBtoF_s.png new file mode 100644 index 0000000000000..2feedcb9d16f5 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetBtoF_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetFtoB_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetFtoB_b.png new file mode 100644 index 0000000000000..6959c1d75eb64 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetFtoB_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetFtoB_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetFtoB_s.png new file mode 100644 index 0000000000000..2dbf9fb70c44a Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/convetFtoB_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/export_rml_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/export_rml_b.png new file mode 100644 index 0000000000000..7d8ac7be2d10f Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/export_rml_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/export_rml_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/export_rml_s.png new file mode 100644 index 0000000000000..dc2c4ee0b1974 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/export_rml_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/expression_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/expression_b.png new file mode 100644 index 0000000000000..9ba6aba023c7f Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/expression_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/expression_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/expression_s.png new file mode 100644 index 0000000000000..bfa6f70091317 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/expression_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/field_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/field_b.png new file mode 100644 index 0000000000000..945b3d26a5c54 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/field_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/field_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/field_s.png new file mode 100644 index 0000000000000..e1a68b906abac Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/field_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/lang_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/lang_b.png new file mode 100644 index 0000000000000..11981237ab43c Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/lang_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/logo_1.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/logo_1.png new file mode 100644 index 0000000000000..63de17bafcf3f Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/logo_1.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/logo_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/logo_s.png new file mode 100644 index 0000000000000..61e92630e5831 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/logo_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/loop_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/loop_b.png new file mode 100644 index 0000000000000..b9ff4e8835c04 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/loop_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/loop_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/loop_s.png new file mode 100644 index 0000000000000..f99b4ef424c4e Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/loop_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/modify_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/modify_b.png new file mode 100644 index 0000000000000..c867b9d1919bd Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/modify_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/modify_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/modify_s.png new file mode 100644 index 0000000000000..b1358179c7608 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/modify_s.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/report_b.bmp b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/report_b.bmp new file mode 100644 index 0000000000000..80652c696919b Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/report_b.bmp differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/report_s.bmp b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/report_s.bmp new file mode 100644 index 0000000000000..79086079d5832 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/report_s.bmp differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/reportb.bmp b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/reportb.bmp new file mode 100644 index 0000000000000..80652c696919b Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/reportb.bmp differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/reports.bmp b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/reports.bmp new file mode 100644 index 0000000000000..79086079d5832 Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/reports.bmp differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/server_b.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/server_b.png new file mode 100644 index 0000000000000..b339a97d2c3dd Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/server_b.png differ diff --git a/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/server_s.png b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/server_s.png new file mode 100644 index 0000000000000..b339a97d2c3dd Binary files /dev/null and b/addons/base_report_designer/plugin/openerp_report_designer/bin/package/images/server_s.png differ diff --git a/addons/base_report_designer/wizard/base_report_designer_modify.py b/addons/base_report_designer/wizard/base_report_designer_modify.py index abef384cd91ce..d3f26bb68d1df 100644 --- a/addons/base_report_designer/wizard/base_report_designer_modify.py +++ b/addons/base_report_designer/wizard/base_report_designer_modify.py @@ -1,151 +1,136 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). All Rights Reserved -# $Id$ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import base64 -import time -import urllib - -from openerp import osv, pooler, tools -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -class base_report_sxw(osv.osv_memory): - """Base Report sxw """ - _name = 'base.report.sxw' - - _columns = { - 'report_id': fields.many2one('ir.actions.report.xml', "Report", required=True,domain=[('report_sxw_content','<>',False)],), - } - - - def get_report(self, cr, uid, ids, context=None): - data = self.read(cr, uid, ids, context=context)[0] - data_obj = self.pool.get('ir.model.data') - id2 = data_obj._get_id(cr, uid, 'base_report_designer', 'view_base_report_file_sxw') - report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) - if id2: - id2 = data_obj.browse(cr, uid, id2, context=context).res_id - return { - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'base.report.file.sxw', - 'views': [(id2, 'form')], - 'view_id': False, - 'type': 'ir.actions.act_window', - 'target': 'new', - } - -base_report_sxw() - -class base_report_file_sxw(osv.osv_memory): - """Base Report File sxw """ - _name = 'base.report.file.sxw' - - def default_get(self, cr, uid, fields, context=None): - """ - To get default values for the object. - - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param fields: List of fields for which we want default values - @param context: A standard dictionary - - @return: A dictionary which of fields with values. - - """ - res = super(base_report_file_sxw, self).default_get(cr, uid, fields, context=context) - report_id1 = self.pool.get('base.report.sxw').search(cr,uid,[]) - data = self.pool.get('base.report.sxw').read(cr, uid, report_id1, context=context)[0] - report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) - if context is None: - context={} - if 'report_id' in fields: - res['report_id'] = data['report_id'] - res['file_sxw'] = base64.encodestring(report.report_sxw_content) - return res - - _columns = { - 'report_id': fields.many2one('ir.actions.report.xml', "Report", readonly=True), - 'file_sxw':fields.binary('Your .SXW file',readonly=True), - 'file_sxw_upload':fields.binary('Your .SXW file',required=True) - } - - def upload_report(self, cr, uid, ids, context=None): - from base_report_designer import openerp_sxw2rml - import StringIO - data=self.read(cr,uid,ids)[0] - sxwval = StringIO.StringIO(base64.decodestring(data['file_sxw_upload'])) - fp = tools.file_open('normalized_oo2rml.xsl',subdir='addons/base_report_designer/openerp_sxw2rml') - newrmlcontent = str(openerp_sxw2rml.sxw2rml(sxwval, xsl=fp.read())) - report = self.pool.get('ir.actions.report.xml').write(cr, uid, [data['report_id']], { - 'report_sxw_content': base64.decodestring(data['file_sxw_upload']), - 'report_rml_content': newrmlcontent - }) - cr.commit() - data_obj = self.pool.get('ir.model.data') - id2 = data_obj._get_id(cr, uid, 'base_report_designer', 'view_base_report_file_rml') - report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) - if id2: - id2 = data_obj.browse(cr, uid, id2, context=context).res_id - return { - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'base.report.rml.save', - 'views': [(id2, 'form')], - 'view_id': False, - 'type': 'ir.actions.act_window', - 'target': 'new', - } -base_report_file_sxw() - -class base_report_rml_save(osv.osv_memory): - """Base Report file Save""" - _name = 'base.report.rml.save' - def default_get(self, cr, uid, fields, context=None): - """ - To get default values for the object. - - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param fields: List of fields for which we want default values - @param context: A standard dictionary - @return: A dictionary which of fields with values. - - """ - - res = super(base_report_rml_save, self).default_get(cr, uid, fields, context=context) - report_id = self.pool.get('base.report.sxw').search(cr,uid,[]) - data = self.pool.get('base.report.file.sxw').read(cr, uid, report_id, context=context)[0] - report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) - - if 'file_rml' in fields: - res['file_rml'] = base64.encodestring(report.report_rml_content) - return res - _columns = { - 'file_rml':fields.binary('Save As'), - } - -base_report_rml_save() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import base64 +import time +import urllib +from openerp import osv, pooler, tools +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class base_report_sxw(osv.osv_memory): + """Base Report sxw """ + _name = 'base.report.sxw' + _columns = {'report_id': fields.many2one('ir.actions.report.xml', 'Report', required=True, domain=[('report_sxw_content', '<>', False)])} + + def get_report(self, cr, uid, ids, context = None): + data = self.read(cr, uid, ids, context=context)[0] + data_obj = self.pool.get('ir.model.data') + id2 = data_obj._get_id(cr, uid, 'base_report_designer', 'view_base_report_file_sxw') + report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) + if id2: + id2 = data_obj.browse(cr, uid, id2, context=context).res_id + return {'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'base.report.file.sxw', + 'views': [(id2, 'form')], + 'view_id': False, + 'type': 'ir.actions.act_window', + 'target': 'new'} + + +base_report_sxw() + +class base_report_file_sxw(osv.osv_memory): + """Base Report File sxw """ + _name = 'base.report.file.sxw' + + def default_get(self, cr, uid, fields, context = None): + """ + To get default values for the object. + + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param fields: List of fields for which we want default values + @param context: A standard dictionary + + @return: A dictionary which of fields with values. + + """ + res = super(base_report_file_sxw, self).default_get(cr, uid, fields, context=context) + report_id1 = self.pool.get('base.report.sxw').search(cr, uid, []) + data = self.pool.get('base.report.sxw').read(cr, uid, report_id1, context=context)[0] + report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) + if context is None: + context = {} + if 'report_id' in fields: + res['report_id'] = data['report_id'] + res['file_sxw'] = base64.encodestring(report.report_sxw_content) + return res + + _columns = {'report_id': fields.many2one('ir.actions.report.xml', 'Report', readonly=True), + 'file_sxw': fields.binary('Your .SXW file', readonly=True), + 'file_sxw_upload': fields.binary('Your .SXW file', required=True)} + + def upload_report(self, cr, uid, ids, context = None): + from base_report_designer import openerp_sxw2rml + import StringIO + data = self.read(cr, uid, ids)[0] + sxwval = StringIO.StringIO(base64.decodestring(data['file_sxw_upload'])) + fp = tools.file_open('normalized_oo2rml.xsl', subdir='addons/base_report_designer/openerp_sxw2rml') + newrmlcontent = str(openerp_sxw2rml.sxw2rml(sxwval, xsl=fp.read())) + report = self.pool.get('ir.actions.report.xml').write(cr, uid, [data['report_id']], {'report_sxw_content': base64.decodestring(data['file_sxw_upload']), + 'report_rml_content': newrmlcontent}) + cr.commit() + data_obj = self.pool.get('ir.model.data') + id2 = data_obj._get_id(cr, uid, 'base_report_designer', 'view_base_report_file_rml') + report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) + if id2: + id2 = data_obj.browse(cr, uid, id2, context=context).res_id + return {'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'base.report.rml.save', + 'views': [(id2, 'form')], + 'view_id': False, + 'type': 'ir.actions.act_window', + 'target': 'new'} + + +base_report_file_sxw() + +class base_report_rml_save(osv.osv_memory): + """Base Report file Save""" + _name = 'base.report.rml.save' + + def default_get(self, cr, uid, fields, context = None): + """ + To get default values for the object. + + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param fields: List of fields for which we want default values + @param context: A standard dictionary + @return: A dictionary which of fields with values. + + """ + res = super(base_report_rml_save, self).default_get(cr, uid, fields, context=context) + report_id = self.pool.get('base.report.sxw').search(cr, uid, []) + data = self.pool.get('base.report.file.sxw').read(cr, uid, report_id, context=context)[0] + report = self.pool.get('ir.actions.report.xml').browse(cr, uid, data['report_id'], context=context) + if 'file_rml' in fields: + res['file_rml'] = base64.encodestring(report.report_rml_content) + return res + + _columns = {'file_rml': fields.binary('Save As')} + + +base_report_rml_save() \ No newline at end of file diff --git a/addons/base_setup/base_setup.py b/addons/base_setup/base_setup.py index 3e7c1d1f84652..04a9e5a7e5b5f 100644 --- a/addons/base_setup/base_setup.py +++ b/addons/base_setup/base_setup.py @@ -1,100 +1,101 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import simplejson -import cgi -from openerp import pooler -from openerp import tools -from openerp.osv import fields, osv -from openerp.tools.translate import _ -from lxml import etree - -# Specify Your Terminology will move to 'partner' module -class specify_partner_terminology(osv.osv_memory): - _name = 'base.setup.terminology' - _inherit = 'res.config' - _columns = { - 'partner': fields.selection([ - ('Customer','Customer'), - ('Client','Client'), - ('Member','Member'), - ('Patient','Patient'), - ('Partner','Partner'), - ('Donor','Donor'), - ('Guest','Guest'), - ('Tenant','Tenant') - ], 'How do you call a Customer', required=True ), - } - _defaults={ - 'partner' :'Customer', - } - - def make_translations(self, cr, uid, ids, name, type, src, value, res_id=0, context=None): - trans_obj = self.pool.get('ir.translation') - user_obj = self.pool.get('res.users') - context_lang = user_obj.browse(cr, uid, uid, context=context).lang - existing_trans_ids = trans_obj.search(cr, uid, [('name','=',name), ('lang','=',context_lang), ('type','=',type), ('src','=',src), ('res_id','=',res_id)]) - if existing_trans_ids: - trans_obj.write(cr, uid, existing_trans_ids, {'value': value}, context=context) - else: - create_id = trans_obj.create(cr, uid, {'name': name,'lang': context_lang, 'type': type, 'src': src, 'value': value , 'res_id': res_id}, context=context) - return {} - - def execute(self, cr, uid, ids, context=None): - def _case_insensitive_replace(ref_string, src, value): - import re - pattern = re.compile(src, re.IGNORECASE) - return pattern.sub(_(value), _(ref_string)) - trans_obj = self.pool.get('ir.translation') - fields_obj = self.pool.get('ir.model.fields') - menu_obj = self.pool.get('ir.ui.menu') - act_window_obj = self.pool.get('ir.actions.act_window') - for o in self.browse(cr, uid, ids, context=context): - #translate label of field - field_ids = fields_obj.search(cr, uid, [('field_description','ilike','Customer')]) - for f_id in fields_obj.browse(cr ,uid, field_ids, context=context): - field_ref = f_id.model_id.model + ',' + f_id.name - self.make_translations(cr, uid, ids, field_ref, 'field', f_id.field_description, _case_insensitive_replace(f_id.field_description,'Customer',o.partner), context=context) - #translate help tooltip of field - for obj in self.pool.models.values(): - for field_name, field_rec in obj._columns.items(): - if field_rec.help.lower().count('customer'): - field_ref = obj._name + ',' + field_name - self.make_translations(cr, uid, ids, field_ref, 'help', field_rec.help, _case_insensitive_replace(field_rec.help,'Customer',o.partner), context=context) - #translate menuitems - menu_ids = menu_obj.search(cr,uid, [('name','ilike','Customer')]) - for m_id in menu_obj.browse(cr, uid, menu_ids, context=context): - menu_name = m_id.name - menu_ref = 'ir.ui.menu' + ',' + 'name' - self.make_translations(cr, uid, ids, menu_ref, 'model', menu_name, _case_insensitive_replace(menu_name,'Customer',o.partner), res_id=m_id.id, context=context) - #translate act window name - act_window_ids = act_window_obj.search(cr, uid, [('name','ilike','Customer')]) - for act_id in act_window_obj.browse(cr ,uid, act_window_ids, context=context): - act_ref = 'ir.actions.act_window' + ',' + 'name' - self.make_translations(cr, uid, ids, act_ref, 'model', act_id.name, _case_insensitive_replace(act_id.name,'Customer',o.partner), res_id=act_id.id, context=context) - #translate act window tooltips - act_window_ids = act_window_obj.search(cr, uid, [('help','ilike','Customer')]) - for act_id in act_window_obj.browse(cr ,uid, act_window_ids, context=context): - act_ref = 'ir.actions.act_window' + ',' + 'help' - self.make_translations(cr, uid, ids, act_ref, 'model', act_id.help, _case_insensitive_replace(act_id.help,'Customer',o.partner), res_id=act_id.id, context=context) - return {} - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import simplejson +import cgi +from openerp import pooler +from openerp import tools +from openerp.osv import fields, osv +from openerp.tools.translate import _ +from lxml import etree + +class specify_partner_terminology(osv.osv_memory): + _name = 'base.setup.terminology' + _inherit = 'res.config' + _columns = {'partner': fields.selection([('Customer', 'Customer'), + ('Client', 'Client'), + ('Member', 'Member'), + ('Patient', 'Patient'), + ('Partner', 'Partner'), + ('Donor', 'Donor'), + ('Guest', 'Guest'), + ('Tenant', 'Tenant')], 'How do you call a Customer', required=True)} + _defaults = {'partner': 'Customer'} + + def make_translations(self, cr, uid, ids, name, type, src, value, res_id = 0, context = None): + trans_obj = self.pool.get('ir.translation') + user_obj = self.pool.get('res.users') + context_lang = user_obj.browse(cr, uid, uid, context=context).lang + existing_trans_ids = trans_obj.search(cr, uid, [('name', '=', name), + ('lang', '=', context_lang), + ('type', '=', type), + ('src', '=', src), + ('res_id', '=', res_id)]) + if existing_trans_ids: + trans_obj.write(cr, uid, existing_trans_ids, {'value': value}, context=context) + else: + create_id = trans_obj.create(cr, uid, {'name': name, + 'lang': context_lang, + 'type': type, + 'src': src, + 'value': value, + 'res_id': res_id}, context=context) + return {} + + def execute(self, cr, uid, ids, context = None): + + def _case_insensitive_replace(ref_string, src, value): + import re + pattern = re.compile(src, re.IGNORECASE) + return pattern.sub(_(value), _(ref_string)) + + trans_obj = self.pool.get('ir.translation') + fields_obj = self.pool.get('ir.model.fields') + menu_obj = self.pool.get('ir.ui.menu') + act_window_obj = self.pool.get('ir.actions.act_window') + for o in self.browse(cr, uid, ids, context=context): + field_ids = fields_obj.search(cr, uid, [('field_description', 'ilike', 'Customer')]) + for f_id in fields_obj.browse(cr, uid, field_ids, context=context): + field_ref = f_id.model_id.model + ',' + f_id.name + self.make_translations(cr, uid, ids, field_ref, 'field', f_id.field_description, _case_insensitive_replace(f_id.field_description, 'Customer', o.partner), context=context) + + for obj in self.pool.models.values(): + for field_name, field_rec in obj._columns.items(): + if field_rec.help.lower().count('customer'): + field_ref = obj._name + ',' + field_name + self.make_translations(cr, uid, ids, field_ref, 'help', field_rec.help, _case_insensitive_replace(field_rec.help, 'Customer', o.partner), context=context) + + menu_ids = menu_obj.search(cr, uid, [('name', 'ilike', 'Customer')]) + for m_id in menu_obj.browse(cr, uid, menu_ids, context=context): + menu_name = m_id.name + menu_ref = 'ir.ui.menu,name' + self.make_translations(cr, uid, ids, menu_ref, 'model', menu_name, _case_insensitive_replace(menu_name, 'Customer', o.partner), res_id=m_id.id, context=context) + + act_window_ids = act_window_obj.search(cr, uid, [('name', 'ilike', 'Customer')]) + for act_id in act_window_obj.browse(cr, uid, act_window_ids, context=context): + act_ref = 'ir.actions.act_window' + ',' + 'name' + self.make_translations(cr, uid, ids, act_ref, 'model', act_id.name, _case_insensitive_replace(act_id.name, 'Customer', o.partner), res_id=act_id.id, context=context) + + act_window_ids = act_window_obj.search(cr, uid, [('help', 'ilike', 'Customer')]) + for act_id in act_window_obj.browse(cr, uid, act_window_ids, context=context): + act_ref = 'ir.actions.act_window' + ',' + 'help' + self.make_translations(cr, uid, ids, act_ref, 'model', act_id.help, _case_insensitive_replace(act_id.help, 'Customer', o.partner), res_id=act_id.id, context=context) + + return {} \ No newline at end of file diff --git a/addons/base_setup/images/base_setup1.jpeg b/addons/base_setup/images/base_setup1.jpeg new file mode 100644 index 0000000000000..59b75f7b0bdcd Binary files /dev/null and b/addons/base_setup/images/base_setup1.jpeg differ diff --git a/addons/base_setup/images/base_setup2.jpeg b/addons/base_setup/images/base_setup2.jpeg new file mode 100644 index 0000000000000..654260d21070a Binary files /dev/null and b/addons/base_setup/images/base_setup2.jpeg differ diff --git a/addons/base_setup/images/base_setup3.jpeg b/addons/base_setup/images/base_setup3.jpeg new file mode 100644 index 0000000000000..6244e4d580d2c Binary files /dev/null and b/addons/base_setup/images/base_setup3.jpeg differ diff --git a/addons/base_setup/images/base_setup4.jpeg b/addons/base_setup/images/base_setup4.jpeg new file mode 100644 index 0000000000000..5ad768af1f598 Binary files /dev/null and b/addons/base_setup/images/base_setup4.jpeg differ diff --git a/addons/base_setup/res_config.py b/addons/base_setup/res_config.py index 33c5aa863bf9b..95b5da1f12a89 100644 --- a/addons/base_setup/res_config.py +++ b/addons/base_setup/res_config.py @@ -1,79 +1,51 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (C) 2004-2012 OpenERP S.A. (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv - -class base_config_settings(osv.osv_memory): - _name = 'base.config.settings' - _inherit = 'res.config.settings' - _columns = { - 'module_multi_company': fields.boolean('Manage multiple companies', - help="""Work in multi-company environments, with appropriate security access between companies. - This installs the module multi_company."""), - 'module_share': fields.boolean('Allow documents sharing', - help="""Share or embbed any screen of openerp."""), - 'module_portal': fields.boolean('Activate the customer portal', - help="""Give your customers access to their documents."""), - 'module_portal_anonymous': fields.boolean('Activate the public portal', - help="""Enable the public part of openerp, openerp becomes a public website."""), - 'module_auth_oauth': fields.boolean('Use external authentication providers, sign in with google, facebook, ...'), - 'module_base_import': fields.boolean("Allow users to import data from CSV files"), - } - - def open_company(self, cr, uid, ids, context=None): - user = self.pool.get('res.users').browse(cr, uid, uid, context) - return { - 'type': 'ir.actions.act_window', - 'name': 'Your Company', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'res.company', - 'res_id': user.company_id.id, - 'target': 'current', - } - -# Preferences wizard for Sales & CRM. -# It is defined here because it is inherited independently in modules sale, crm, -# plugin_outlook and plugin_thunderbird. -class sale_config_settings(osv.osv_memory): - _name = 'sale.config.settings' - _inherit = 'res.config.settings' - _columns = { - 'module_web_linkedin': fields.boolean('Get contacts automatically from linkedIn', - help="""When you create a new contact (person or company), you will be able to load all the data from LinkedIn (photos, address, etc)."""), - 'module_crm': fields.boolean('CRM'), - 'module_sale' : fields.boolean('SALE'), - 'module_plugin_thunderbird': fields.boolean('Enable Thunderbird plug-in', - help="""The plugin allows you archive email and its attachments to the selected - OpenERP objects. You can select a partner, or a lead and - attach the selected mail as a .eml file in - the attachment of a selected record. You can create documents for CRM Lead, - Partner from the selected emails. - This installs the module plugin_thunderbird."""), - 'module_plugin_outlook': fields.boolean('Enable Outlook plug-in', - help="""The Outlook plugin allows you to select an object that you would like to add - to your email and its attachments from MS Outlook. You can select a partner, - or a lead object and archive a selected - email into an OpenERP mail message with attachments. - This installs the module plugin_outlook."""), - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv + +class base_config_settings(osv.osv_memory): + _name = 'base.config.settings' + _inherit = 'res.config.settings' + _columns = {'module_multi_company': fields.boolean('Manage multiple companies', help='Work in multi-company environments, with appropriate security access between companies.\n This installs the module multi_company.'), + 'module_share': fields.boolean('Allow documents sharing', help='Share or embbed any screen of openerp.'), + 'module_portal': fields.boolean('Activate the customer portal', help='Give your customers access to their documents.'), + 'module_portal_anonymous': fields.boolean('Activate the public portal', help='Enable the public part of openerp, openerp becomes a public website.'), + 'module_auth_oauth': fields.boolean('Use external authentication providers, sign in with google, facebook, ...'), + 'module_base_import': fields.boolean('Allow users to import data from CSV files')} + + def open_company(self, cr, uid, ids, context = None): + user = self.pool.get('res.users').browse(cr, uid, uid, context) + return {'type': 'ir.actions.act_window', + 'name': 'Your Company', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'res.company', + 'res_id': user.company_id.id, + 'target': 'current'} + + +class sale_config_settings(osv.osv_memory): + _name = 'sale.config.settings' + _inherit = 'res.config.settings' + _columns = {'module_web_linkedin': fields.boolean('Get contacts automatically from linkedIn', help='When you create a new contact (person or company), you will be able to load all the data from LinkedIn (photos, address, etc).'), + 'module_crm': fields.boolean('CRM'), + 'module_sale': fields.boolean('SALE'), + 'module_plugin_thunderbird': fields.boolean('Enable Thunderbird plug-in', help='The plugin allows you archive email and its attachments to the selected\n OpenERP objects. You can select a partner, or a lead and\n attach the selected mail as a .eml file in\n the attachment of a selected record. You can create documents for CRM Lead,\n Partner from the selected emails.\n This installs the module plugin_thunderbird.'), + 'module_plugin_outlook': fields.boolean('Enable Outlook plug-in', help='The Outlook plugin allows you to select an object that you would like to add\n to your email and its attachments from MS Outlook. You can select a partner,\n or a lead object and archive a selected\n email into an OpenERP mail message with attachments.\n This installs the module plugin_outlook.')} \ No newline at end of file diff --git a/addons/base_status/base_stage.py b/addons/base_status/base_stage.py index 480946e3c52ea..fa61960d2ad28 100644 --- a/addons/base_status/base_stage.py +++ b/addons/base_status/base_stage.py @@ -1,264 +1,269 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -class base_stage(object): - """ Base utility mixin class for objects willing to manage their stages. - Object that inherit from this class should inherit from mailgate.thread - to have access to the mail gateway, as well as Chatter. Objects - subclassing this class should define the following colums: - - ``date_open`` (datetime field) - - ``date_closed`` (datetime field) - - ``user_id`` (many2one to res.users) - - ``partner_id`` (many2one to res.partner) - - ``stage_id`` (many2one to a stage definition model) - - ``state`` (selection field, related to the stage_id.state) - """ - - def _get_default_partner(self, cr, uid, context=None): - """ Gives id of partner for current user - :param context: if portal not in context returns False - """ - if context is None: - context = {} - if context.get('portal'): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - return user.partner_id.id - return False - - def _get_default_email(self, cr, uid, context=None): - """ Gives default email address for current user - :param context: if portal not in context returns False - """ - if context is None: - context = {} - if context.get('portal'): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - return user.email - return False - - def _get_default_user(self, cr, uid, context=None): - """ Gives current user id - :param context: if portal not in context returns False - """ - if context is None: - context = {} - if not context or context.get('portal'): - return False - return uid - - def onchange_partner_address_id(self, cr, uid, ids, add, email=False, context=None): - """ This function returns value of partner email based on Partner Address - :param add: Id of Partner's address - :param email: Partner's email ID - """ - data = {'value': {'email_from': False, 'phone':False}} - if add: - address = self.pool.get('res.partner').browse(cr, uid, add) - data['value'] = {'partner_name': address and address.name or False, - 'email_from': address and address.email or False, - 'phone': address and address.phone or False, - 'street': address and address.street or False, - 'street2': address and address.street2 or False, - 'city': address and address.city or False, - 'state_id': address.state_id and address.state_id.id or False, - 'zip': address and address.zip or False, - 'country_id': address.country_id and address.country_id.id or False, - } - fields = self.fields_get(cr, uid, context=context or {}) - for key in data['value'].keys(): - if key not in fields: - del data['value'][key] - return data - - def onchange_partner_id(self, cr, uid, ids, part, email=False): - """ This function returns value of partner address based on partner - :param part: Partner's id - :param email: Partner's email ID - """ - data={} - if part: - addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) - data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value']) - return {'value': data} - - def _get_default_section_id(self, cr, uid, context=None): - """ Gives default section """ - return False - - def _get_default_stage_id(self, cr, uid, context=None): - """ Gives default stage_id """ - return self.stage_find(cr, uid, [], None, [('state', '=', 'draft')], context=context) - - def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None): - """ Find stage, with a given (optional) domain on the search, - ordered by the order parameter. If several stages match the - search criterions, the first one will be returned, according - to the requested search order. - This method is meant to be overriden by subclasses. That way - specific behaviors can be achieved for every class inheriting - from base_stage. - - :param cases: browse_record of cases - :param section_id: section limitating the search, given for - a generic search (for example default search). - A section models concepts such as Sales team - (for CRM), ou departments (for HR). - :param domain: a domain on the search of stages - :param order: order of the search - """ - return False - - def stage_set_with_state_name(self, cr, uid, cases, state_name, context=None): - """ Set a new stage, with a state_name instead of a stage_id - :param cases: browse_record of cases - """ - if isinstance(cases, (int, long)): - cases = self.browse(cr, uid, cases, context=context) - for case in cases: - stage_id = self.stage_find(cr, uid, [case], None, [('state', '=', state_name)], context=context) - if stage_id: - self.stage_set(cr, uid, [case.id], stage_id, context=context) - return True - - def stage_set(self, cr, uid, ids, stage_id, context=None): - """ Set the new stage. This methods is the right method to call - when changing states. It also checks whether an onchange is - defined, and execute it. - """ - value = {} - if hasattr(self, 'onchange_stage_id'): - value = self.onchange_stage_id(cr, uid, ids, stage_id, context=context)['value'] - value['stage_id'] = stage_id - return self.write(cr, uid, ids, value, context=context) - - def stage_change(self, cr, uid, ids, op, order, context=None): - """ Change the stage and take the next one, based on a condition - writen for the 'sequence' field and an operator. This methods - checks whether the case has a current stage, and takes its - sequence. Otherwise, a default 0 sequence is chosen and this - method will therefore choose the first available stage. - For example if op is '>' and current stage has a sequence of - 10, this will call stage_find, with [('sequence', '>', '10')]. - """ - for case in self.browse(cr, uid, ids, context=context): - seq = 0 - if case.stage_id: - seq = case.stage_id.sequence or 0 - section_id = None - next_stage_id = self.stage_find(cr, uid, [case], None, [('sequence', op, seq)],order, context=context) - if next_stage_id: - return self.stage_set(cr, uid, [case.id], next_stage_id, context=context) - return False - - def stage_next(self, cr, uid, ids, context=None): - """ This function computes next stage for case from its current stage - using available stage for that case type - """ - return self.stage_change(cr, uid, ids, '>','sequence', context) - - def stage_previous(self, cr, uid, ids, context=None): - """ This function computes previous stage for case from its current - stage using available stage for that case type - """ - return self.stage_change(cr, uid, ids, '<', 'sequence desc', context) - - def copy(self, cr, uid, id, default=None, context=None): - """ Overrides orm copy method to avoid copying messages, - as well as date_closed and date_open columns if they - exist.""" - if default is None: - default = {} - - if hasattr(self, '_columns'): - if self._columns.get('date_closed'): - default.setdefault('date_closed', False) - if self._columns.get('date_open'): - default.setdefault('date_open', False ) - return super(base_stage, self).copy(cr, uid, id, default, context=context) - - def case_escalate(self, cr, uid, ids, context=None): - """ Escalates case to parent level """ - for case in self.browse(cr, uid, ids, context=context): - data = {'active': True} - if case.section_id.parent_id: - data['section_id'] = case.section_id.parent_id.id - if case.section_id.parent_id.change_responsible: - if case.section_id.parent_id.user_id: - data['user_id'] = case.section_id.parent_id.user_id.id - else: - raise osv.except_osv(_('Error!'), _("You are already at the top level of your sales-team category.\nTherefore you cannot escalate furthermore.")) - self.write(cr, uid, [case.id], data, context=context) - return True - - def case_open(self, cr, uid, ids, context=None): - """ Opens case """ - cases = self.browse(cr, uid, ids, context=context) - for case in cases: - data = {'active': True} - if not case.user_id: - data['user_id'] = uid - self.case_set(cr, uid, [case.id], 'open', data, context=context) - return True - - def case_close(self, cr, uid, ids, context=None): - """ Closes case """ - return self.case_set(cr, uid, ids, 'done', {'active': True, 'date_closed': fields.datetime.now()}, context=context) - - def case_cancel(self, cr, uid, ids, context=None): - """ Cancels case """ - return self.case_set(cr, uid, ids, 'cancel', {'active': True}, context=context) - - def case_pending(self, cr, uid, ids, context=None): - """ Set case as pending """ - return self.case_set(cr, uid, ids, 'pending', {'active': True}, context=context) - - def case_reset(self, cr, uid, ids, context=None): - """ Resets case as draft """ - return self.case_set(cr, uid, ids, 'draft', {'active': True}, context=context) - - def case_set(self, cr, uid, ids, new_state_name=None, values_to_update=None, new_stage_id=None, context=None): - """ Generic method for setting case. This methods wraps the update - of the record. - - :params new_state_name: the new state of the record; this method - will call ``stage_set_with_state_name`` - that will find the stage matching the - new state, using the ``stage_find`` method. - :params new_stage_id: alternatively, you may directly give the - new stage of the record - :params state_name: the new value of the state, such as - 'draft' or 'close'. - :params update_values: values that will be added with the state - update when writing values to the record. - """ - cases = self.browse(cr, uid, ids, context=context) - # 1. update the stage - if new_state_name: - self.stage_set_with_state_name(cr, uid, cases, new_state_name, context=context) - elif not (new_stage_id is None): - self.stage_set(cr, uid, ids, new_stage_id, context=context) - # 2. update values - if values_to_update: - self.write(cr, uid, ids, values_to_update, context=context) - return True +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class base_stage(object): + """ Base utility mixin class for objects willing to manage their stages. + Object that inherit from this class should inherit from mailgate.thread + to have access to the mail gateway, as well as Chatter. Objects + subclassing this class should define the following colums: + - ``date_open`` (datetime field) + - ``date_closed`` (datetime field) + - ``user_id`` (many2one to res.users) + - ``partner_id`` (many2one to res.partner) + - ``stage_id`` (many2one to a stage definition model) + - ``state`` (selection field, related to the stage_id.state) + """ + + def _get_default_partner(self, cr, uid, context = None): + """ Gives id of partner for current user + :param context: if portal not in context returns False + """ + if context is None: + context = {} + if context.get('portal'): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + return user.partner_id.id + else: + return False + + def _get_default_email(self, cr, uid, context = None): + """ Gives default email address for current user + :param context: if portal not in context returns False + """ + if context is None: + context = {} + if context.get('portal'): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + return user.email + else: + return False + + def _get_default_user(self, cr, uid, context = None): + """ Gives current user id + :param context: if portal not in context returns False + """ + if context is None: + context = {} + if not context or context.get('portal'): + return False + else: + return uid + + def onchange_partner_address_id(self, cr, uid, ids, add, email = False, context = None): + """ This function returns value of partner email based on Partner Address + :param add: Id of Partner's address + :param email: Partner's email ID + """ + data = {'value': {'email_from': False, + 'phone': False}} + if add: + address = self.pool.get('res.partner').browse(cr, uid, add) + data['value'] = {'partner_name': address and address.name or False, + 'email_from': address and address.email or False, + 'phone': address and address.phone or False, + 'street': address and address.street or False, + 'street2': address and address.street2 or False, + 'city': address and address.city or False, + 'state_id': address.state_id and address.state_id.id or False, + 'zip': address and address.zip or False, + 'country_id': address.country_id and address.country_id.id or False} + fields = self.fields_get(cr, uid, context=context or {}) + for key in data['value'].keys(): + if key not in fields: + del data['value'][key] + + return data + + def onchange_partner_id(self, cr, uid, ids, part, email = False): + """ This function returns value of partner address based on partner + :param part: Partner's id + :param email: Partner's email ID + """ + data = {} + if part: + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) + data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value']) + return {'value': data} + + def _get_default_section_id(self, cr, uid, context = None): + """ Gives default section """ + return False + + def _get_default_stage_id(self, cr, uid, context = None): + """ Gives default stage_id """ + return self.stage_find(cr, uid, [], None, [('state', '=', 'draft')], context=context) + + def stage_find(self, cr, uid, cases, section_id, domain = [], order = 'sequence', context = None): + """ Find stage, with a given (optional) domain on the search, + ordered by the order parameter. If several stages match the + search criterions, the first one will be returned, according + to the requested search order. + This method is meant to be overriden by subclasses. That way + specific behaviors can be achieved for every class inheriting + from base_stage. + + :param cases: browse_record of cases + :param section_id: section limitating the search, given for + a generic search (for example default search). + A section models concepts such as Sales team + (for CRM), ou departments (for HR). + :param domain: a domain on the search of stages + :param order: order of the search + """ + return False + + def stage_set_with_state_name(self, cr, uid, cases, state_name, context = None): + """ Set a new stage, with a state_name instead of a stage_id + :param cases: browse_record of cases + """ + if isinstance(cases, (int, long)): + cases = self.browse(cr, uid, cases, context=context) + for case in cases: + stage_id = self.stage_find(cr, uid, [case], None, [('state', '=', state_name)], context=context) + if stage_id: + self.stage_set(cr, uid, [case.id], stage_id, context=context) + + return True + + def stage_set(self, cr, uid, ids, stage_id, context = None): + """ Set the new stage. This methods is the right method to call + when changing states. It also checks whether an onchange is + defined, and execute it. + """ + value = {} + if hasattr(self, 'onchange_stage_id'): + value = self.onchange_stage_id(cr, uid, ids, stage_id, context=context)['value'] + value['stage_id'] = stage_id + return self.write(cr, uid, ids, value, context=context) + + def stage_change(self, cr, uid, ids, op, order, context = None): + """ Change the stage and take the next one, based on a condition + writen for the 'sequence' field and an operator. This methods + checks whether the case has a current stage, and takes its + sequence. Otherwise, a default 0 sequence is chosen and this + method will therefore choose the first available stage. + For example if op is '>' and current stage has a sequence of + 10, this will call stage_find, with [('sequence', '>', '10')]. + """ + for case in self.browse(cr, uid, ids, context=context): + seq = 0 + if case.stage_id: + seq = case.stage_id.sequence or 0 + section_id = None + next_stage_id = self.stage_find(cr, uid, [case], None, [('sequence', op, seq)], order, context=context) + if next_stage_id: + return self.stage_set(cr, uid, [case.id], next_stage_id, context=context) + + return False + + def stage_next(self, cr, uid, ids, context = None): + """ This function computes next stage for case from its current stage + using available stage for that case type + """ + return self.stage_change(cr, uid, ids, '>', 'sequence', context) + + def stage_previous(self, cr, uid, ids, context = None): + """ This function computes previous stage for case from its current + stage using available stage for that case type + """ + return self.stage_change(cr, uid, ids, '<', 'sequence desc', context) + + def copy(self, cr, uid, id, default = None, context = None): + """ Overrides orm copy method to avoid copying messages, + as well as date_closed and date_open columns if they + exist.""" + if default is None: + default = {} + if hasattr(self, '_columns'): + if self._columns.get('date_closed'): + default.update({'date_closed': False}) + if self._columns.get('date_open'): + default.update({'date_open': False}) + return super(base_stage, self).copy(cr, uid, id, default, context=context) + + def case_escalate(self, cr, uid, ids, context = None): + """ Escalates case to parent level """ + for case in self.browse(cr, uid, ids, context=context): + data = {'active': True} + if case.section_id.parent_id: + data['section_id'] = case.section_id.parent_id.id + if case.section_id.parent_id.change_responsible: + if case.section_id.parent_id.user_id: + data['user_id'] = case.section_id.parent_id.user_id.id + else: + raise osv.except_osv(_('Error!'), _('You are already at the top level of your sales-team category.\nTherefore you cannot escalate furthermore.')) + self.write(cr, uid, [case.id], data, context=context) + + return True + + def case_open(self, cr, uid, ids, context = None): + """ Opens case """ + cases = self.browse(cr, uid, ids, context=context) + for case in cases: + data = {'active': True} + if not case.user_id: + data['user_id'] = uid + self.case_set(cr, uid, [case.id], 'open', data, context=context) + + return True + + def case_close(self, cr, uid, ids, context = None): + """ Closes case """ + return self.case_set(cr, uid, ids, 'done', {'active': True, + 'date_closed': fields.datetime.now()}, context=context) + + def case_cancel(self, cr, uid, ids, context = None): + """ Cancels case """ + return self.case_set(cr, uid, ids, 'cancel', {'active': True}, context=context) + + def case_pending(self, cr, uid, ids, context = None): + """ Set case as pending """ + return self.case_set(cr, uid, ids, 'pending', {'active': True}, context=context) + + def case_reset(self, cr, uid, ids, context = None): + """ Resets case as draft """ + return self.case_set(cr, uid, ids, 'draft', {'active': True}, context=context) + + def case_set(self, cr, uid, ids, new_state_name = None, values_to_update = None, new_stage_id = None, context = None): + """ Generic method for setting case. This methods wraps the update + of the record. + + :params new_state_name: the new state of the record; this method + will call ``stage_set_with_state_name`` + that will find the stage matching the + new state, using the ``stage_find`` method. + :params new_stage_id: alternatively, you may directly give the + new stage of the record + :params state_name: the new value of the state, such as + 'draft' or 'close'. + :params update_values: values that will be added with the state + update when writing values to the record. + """ + cases = self.browse(cr, uid, ids, context=context) + if new_state_name: + self.stage_set_with_state_name(cr, uid, cases, new_state_name, context=context) + elif new_stage_id is not None: + self.stage_set(cr, uid, ids, new_stage_id, context=context) + if values_to_update: + self.write(cr, uid, ids, values_to_update, context=context) + return True \ No newline at end of file diff --git a/addons/base_status/base_state.py b/addons/base_status/base_state.py index 3fe59ecbd2182..c8fb74a8f0ef9 100644 --- a/addons/base_status/base_state.py +++ b/addons/base_status/base_state.py @@ -1,201 +1,208 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -class base_state(object): - """ Base utility mixin class for objects willing to manage their state. - Object subclassing this class should define the following colums: - - ``date_open`` (datetime field) - - ``date_closed`` (datetime field) - - ``user_id`` (many2one to res.users) - - ``partner_id`` (many2one to res.partner) - - ``email_from`` (char field) - - ``state`` (selection field) - """ - - def _get_default_partner(self, cr, uid, context=None): - """ Gives id of partner for current user - :param context: if portal not in context returns False - """ - if context is None: - context = {} - if not context or not context.get('portal'): - return False - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if hasattr(user, 'partner_address_id') and user.partner_address_id: - return user.partner_address_id - return user.company_id.partner_id.id - - def _get_default_email(self, cr, uid, context=None): - """ Gives default email address for current user - :param context: if portal not in context returns False - """ - if context is None: - context = {} - if not context or not context.get('portal'): - return False - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - return user.email - - def _get_default_user(self, cr, uid, context=None): - """ Gives current user id - :param context: if portal not in context returns False - """ - if context is None: - context = {} - if not context or not context.get('portal'): - return False - return uid - - def onchange_partner_address_id(self, cr, uid, ids, add, email=False): - """ This function returns value of partner email based on Partner Address - :param add: Id of Partner's address - :param email: Partner's email ID - """ - data = {'value': {'email_from': False, 'phone':False}} - if add: - address = self.pool.get('res.partner').browse(cr, uid, add) - data['value'] = {'email_from': address and address.email or False , - 'phone': address and address.phone or False} - if 'phone' not in self._columns: - del data['value']['phone'] - return data - - def onchange_partner_id(self, cr, uid, ids, part, email=False): - """ This function returns value of partner address based on partner - :param part: Partner's id - :param email: Partner's email ID - """ - data={} - if part: - addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) - data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value']) - return {'value': data} - - def case_escalate(self, cr, uid, ids, context=None): - """ Escalates case to parent level """ - cases = self.browse(cr, uid, ids, context=context) - cases[0].state # fill browse record cache, for _action having old and new values - data = {'active': True} - for case in cases: - parent_id = case.section_id.parent_id - if parent_id: - data['section_id'] = parent_id.id - if parent_id.change_responsible and parent_id.user_id: - data['user_id'] = parent_id.user_id.id - else: - raise osv.except_osv(_('Error!'), _('You can not escalate, you are already at the top level regarding your sales-team category.')) - self.write(cr, uid, [case.id], data, context=context) - case.case_escalate_send_note(parent_id, context=context) - return True - - def case_open(self, cr, uid, ids, context=None): - """ Opens case """ - cases = self.browse(cr, uid, ids, context=context) - for case in cases: - values = {'active': True} - if case.state == 'draft': - values['date_open'] = fields.datetime.now() - if not case.user_id: - values['user_id'] = uid - self.case_set(cr, uid, [case.id], 'open', values, context=context) - return True - - def case_close(self, cr, uid, ids, context=None): - """ Closes case """ - return self.case_set(cr, uid, ids, 'done', {'date_closed': fields.datetime.now()}, context=context) - - def case_cancel(self, cr, uid, ids, context=None): - """ Cancels case """ - return self.case_set(cr, uid, ids, 'cancel', {'active': True}, context=context) - - def case_pending(self, cr, uid, ids, context=None): - """ Sets case as pending """ - return self.case_set(cr, uid, ids, 'pending', {'active': True}, context=context) - - def case_reset(self, cr, uid, ids, context=None): - """ Resets case as draft """ - return self.case_set(cr, uid, ids, 'draft', {'active': True}, context=context) - - def case_set(self, cr, uid, ids, state_name, update_values=None, context=None): - """ Generic method for setting case. This methods wraps the update - of the record, as well as call to _action and browse_record - case setting to fill the cache. - - :params: state_name: the new value of the state, such as - 'draft' or 'close'. - :params: update_values: values that will be added with the state - update when writing values to the record. - """ - cases = self.browse(cr, uid, ids, context=context) - cases[0].state # fill browse record cache, for _action having old and new values - if update_values is None: - update_values = {} - update_values['state'] = state_name - return self.write(cr, uid, ids, update_values, context=context) - - # ****************************** - # Notifications - # ****************************** - - def case_get_note_msg_prefix(self, cr, uid, id, context=None): - return '' - - def case_open_send_note(self, cr, uid, ids, context=None): - for id in ids: - msg = _('%s has been opened.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], body=msg, context=context) - return True - - def case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None): - for id in ids: - if new_section: - msg = '%s has been escalated to %s.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name) - else: - msg = '%s has been escalated.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], body=msg, context=context) - return True - - def case_close_send_note(self, cr, uid, ids, context=None): - for id in ids: - msg = _('%s has been closed.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], body=msg, context=context) - return True - - def case_cancel_send_note(self, cr, uid, ids, context=None): - for id in ids: - msg = _('%s has been canceled.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], body=msg, context=context) - return True - - def case_pending_send_note(self, cr, uid, ids, context=None): - for id in ids: - msg = _('%s is now pending.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], body=msg, context=context) - return True - - def case_reset_send_note(self, cr, uid, ids, context=None): - for id in ids: - msg = _('%s has been renewed.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_post(cr, uid, [id], body=msg, context=context) - return True +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class base_state(object): + """ Base utility mixin class for objects willing to manage their state. + Object subclassing this class should define the following colums: + - ``date_open`` (datetime field) + - ``date_closed`` (datetime field) + - ``user_id`` (many2one to res.users) + - ``partner_id`` (many2one to res.partner) + - ``email_from`` (char field) + - ``state`` (selection field) + """ + + def _get_default_partner(self, cr, uid, context = None): + """ Gives id of partner for current user + :param context: if portal not in context returns False + """ + if context is None: + context = {} + if not context or not context.get('portal'): + return False + else: + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if hasattr(user, 'partner_address_id') and user.partner_address_id: + return user.partner_address_id + return user.company_id.partner_id.id + + def _get_default_email(self, cr, uid, context = None): + """ Gives default email address for current user + :param context: if portal not in context returns False + """ + if context is None: + context = {} + if not context or not context.get('portal'): + return False + else: + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + return user.email + + def _get_default_user(self, cr, uid, context = None): + """ Gives current user id + :param context: if portal not in context returns False + """ + if context is None: + context = {} + if not context or not context.get('portal'): + return False + else: + return uid + + def onchange_partner_address_id(self, cr, uid, ids, add, email = False): + """ This function returns value of partner email based on Partner Address + :param add: Id of Partner's address + :param email: Partner's email ID + """ + data = {'value': {'email_from': False, + 'phone': False}} + if add: + address = self.pool.get('res.partner').browse(cr, uid, add) + data['value'] = {'email_from': address and address.email or False, + 'phone': address and address.phone or False} + if 'phone' not in self._columns: + del data['value']['phone'] + return data + + def onchange_partner_id(self, cr, uid, ids, part, email = False): + """ This function returns value of partner address based on partner + :param part: Partner's id + :param email: Partner's email ID + """ + data = {} + if part: + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) + data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value']) + return {'value': data} + + def case_escalate(self, cr, uid, ids, context = None): + """ Escalates case to parent level """ + cases = self.browse(cr, uid, ids, context=context) + cases[0].state + data = {'active': True} + for case in cases: + parent_id = case.section_id.parent_id + if parent_id: + data['section_id'] = parent_id.id + if parent_id.change_responsible and parent_id.user_id: + data['user_id'] = parent_id.user_id.id + else: + raise osv.except_osv(_('Error!'), _('You can not escalate, you are already at the top level regarding your sales-team category.')) + self.write(cr, uid, [case.id], data, context=context) + case.case_escalate_send_note(parent_id, context=context) + + return True + + def case_open(self, cr, uid, ids, context = None): + """ Opens case """ + cases = self.browse(cr, uid, ids, context=context) + for case in cases: + values = {'active': True} + if case.state == 'draft': + values['date_open'] = fields.datetime.now() + if not case.user_id: + values['user_id'] = uid + self.case_set(cr, uid, [case.id], 'open', values, context=context) + + return True + + def case_close(self, cr, uid, ids, context = None): + """ Closes case """ + return self.case_set(cr, uid, ids, 'done', {'date_closed': fields.datetime.now()}, context=context) + + def case_cancel(self, cr, uid, ids, context = None): + """ Cancels case """ + return self.case_set(cr, uid, ids, 'cancel', {'active': True}, context=context) + + def case_pending(self, cr, uid, ids, context = None): + """ Sets case as pending """ + return self.case_set(cr, uid, ids, 'pending', {'active': True}, context=context) + + def case_reset(self, cr, uid, ids, context = None): + """ Resets case as draft """ + return self.case_set(cr, uid, ids, 'draft', {'active': True}, context=context) + + def case_set(self, cr, uid, ids, state_name, update_values = None, context = None): + """ Generic method for setting case. This methods wraps the update + of the record, as well as call to _action and browse_record + case setting to fill the cache. + + :params: state_name: the new value of the state, such as + 'draft' or 'close'. + :params: update_values: values that will be added with the state + update when writing values to the record. + """ + cases = self.browse(cr, uid, ids, context=context) + cases[0].state + if update_values is None: + update_values = {} + update_values['state'] = state_name + return self.write(cr, uid, ids, update_values, context=context) + + def case_get_note_msg_prefix(self, cr, uid, id, context = None): + return '' + + def case_open_send_note(self, cr, uid, ids, context = None): + for id in ids: + msg = _('%s has been opened.') % self.case_get_note_msg_prefix(cr, uid, id, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) + + return True + + def case_escalate_send_note(self, cr, uid, ids, new_section = None, context = None): + for id in ids: + if new_section: + msg = '%s has been escalated to %s.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name) + else: + msg = '%s has been escalated.' % self.case_get_note_msg_prefix(cr, uid, id, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) + + return True + + def case_close_send_note(self, cr, uid, ids, context = None): + for id in ids: + msg = _('%s has been closed.') % self.case_get_note_msg_prefix(cr, uid, id, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) + + return True + + def case_cancel_send_note(self, cr, uid, ids, context = None): + for id in ids: + msg = _('%s has been canceled.') % self.case_get_note_msg_prefix(cr, uid, id, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) + + return True + + def case_pending_send_note(self, cr, uid, ids, context = None): + for id in ids: + msg = _('%s is now pending.') % self.case_get_note_msg_prefix(cr, uid, id, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) + + return True + + def case_reset_send_note(self, cr, uid, ids, context = None): + for id in ids: + msg = _('%s has been renewed.') % self.case_get_note_msg_prefix(cr, uid, id, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) + + return True \ No newline at end of file diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py index 1cf76c670e455..91c2318579902 100644 --- a/addons/base_vat/base_vat.py +++ b/addons/base_vat/base_vat.py @@ -1,323 +1,199 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2012 OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -import logging -import string -import datetime -import re -_logger = logging.getLogger(__name__) - -try: - import vatnumber -except ImportError: - _logger.warning("VAT validation partially unavailable because the `vatnumber` Python library cannot be found. " - "Install it to support more countries, for example with `easy_install vatnumber`.") - vatnumber = None - -from openerp.osv import fields, osv -from openerp.tools.misc import ustr -from openerp.tools.translate import _ - -_ref_vat = { - 'at': 'ATU12345675', - 'be': 'BE0477472701', - 'bg': 'BG1234567892', - 'ch': 'CHE-123.456.788 TVA or CH TVA 123456', #Swiss by Yannick Vaucher @ Camptocamp - 'cy': 'CY12345678F', - 'cz': 'CZ12345679', - 'de': 'DE123456788', - 'dk': 'DK12345674', - 'ee': 'EE123456780', - 'el': 'EL12345670', - 'es': 'ESA12345674', - 'fi': 'FI12345671', - 'fr': 'FR32123456789', - 'gb': 'GB123456782', - 'gr': 'GR12345670', - 'hu': 'HU12345676', - 'hr': 'HR01234567896', # Croatia, contributed by Milan Tribuson - 'ie': 'IE1234567FA', - 'it': 'IT12345670017', - 'lt': 'LT123456715', - 'lu': 'LU12345613', - 'lv': 'LV41234567891', - 'mt': 'MT12345634', - 'mx': 'MXABC123456T1B', - 'nl': 'NL123456782B90', - 'no': 'NO123456785', - 'pe': 'PER10254824220 or PED10254824220', - 'pl': 'PL1234567883', - 'pt': 'PT123456789', - 'ro': 'RO1234567897', - 'se': 'SE123456789701', - 'si': 'SI12345679', - 'sk': 'SK0012345675', -} - -class res_partner(osv.osv): - _inherit = 'res.partner' - - def _split_vat(self, vat): - vat_country, vat_number = vat[:2].lower(), vat[2:].replace(' ', '') - return vat_country, vat_number - - def simple_vat_check(self, cr, uid, country_code, vat_number, context=None): - ''' - Check the VAT number depending of the country. - http://sima-pc.com/nif.php - ''' - if not ustr(country_code).encode('utf-8').isalpha(): - return False - check_func_name = 'check_vat_' + country_code - check_func = getattr(self, check_func_name, None) or \ - getattr(vatnumber, check_func_name, None) - if not check_func: - # No VAT validation available, default to check that the country code exists - if country_code.upper() == 'EU': - # Foreign companies that trade with non-enterprises in the EU - # may have a VATIN starting with "EU" instead of a country code. - return True - res_country = self.pool.get('res.country') - return bool(res_country.search(cr, uid, [('code', '=ilike', country_code)], context=context)) - return check_func(vat_number) - - def vies_vat_check(self, cr, uid, country_code, vat_number, context=None): - try: - # Validate against VAT Information Exchange System (VIES) - # see also http://ec.europa.eu/taxation_customs/vies/ - return vatnumber.check_vies(country_code.upper()+vat_number) - except Exception: - # see http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl - # Fault code may contain INVALID_INPUT, SERVICE_UNAVAILABLE, MS_UNAVAILABLE, - # TIMEOUT or SERVER_BUSY. There is no way we can validate the input - # with VIES if any of these arise, including the first one (it means invalid - # country code or empty VAT number), so we fall back to the simple check. - return self.simple_vat_check(cr, uid, country_code, vat_number, context=context) - - def button_check_vat(self, cr, uid, ids, context=None): - if not self.check_vat(cr, uid, ids, context=context): - msg = self._construct_constraint_msg(cr, uid, ids, context=context) - raise osv.except_osv(_('Error!'), msg) - return True - - def check_vat(self, cr, uid, ids, context=None): - user_company = self.pool.get('res.users').browse(cr, uid, uid).company_id - if user_company.vat_check_vies: - # force full VIES online check - check_func = self.vies_vat_check - else: - # quick and partial off-line checksum validation - check_func = self.simple_vat_check - for partner in self.browse(cr, uid, ids, context=context): - if not partner.vat: - continue - vat_country, vat_number = self._split_vat(partner.vat) - if not check_func(cr, uid, vat_country, vat_number, context=context): - return False - return True - - def vat_change(self, cr, uid, ids, value, context=None): - return {'value': {'vat_subjected': bool(value)}} - - _columns = { - 'vat_subjected': fields.boolean('VAT Legal Statement', help="Check this box if the partner is subjected to the VAT. It will be used for the VAT legal statement.") - } - - def _commercial_fields(self, cr, uid, context=None): - return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['vat_subjected'] - - def _construct_constraint_msg(self, cr, uid, ids, context=None): - def default_vat_check(cn, vn): - # by default, a VAT number is valid if: - # it starts with 2 letters - # has more than 3 characters - return cn[0] in string.ascii_lowercase and cn[1] in string.ascii_lowercase - vat_country, vat_number = self._split_vat(self.browse(cr, uid, ids)[0].vat) - vat_no = "'CC##' (CC=Country Code, ##=VAT Number)" - if default_vat_check(vat_country, vat_number): - vat_no = _ref_vat[vat_country] if vat_country in _ref_vat else vat_no - if self.pool['res.users'].browse(cr, uid, uid).company_id.vat_check_vies: - return '\n' + _('This VAT number either failed the VIES VAT validation check or did not respect the expected format %s.') % vat_no - return '\n' + _('This VAT number does not seem to be valid.\nNote: the expected format is %s') % vat_no - - _constraints = [(check_vat, _construct_constraint_msg, ["vat"])] - - - __check_vat_ch_re1 = re.compile(r'(MWST|TVA|IVA)[0-9]{6}$') - __check_vat_ch_re2 = re.compile(r'E([0-9]{9}|-[0-9]{3}\.[0-9]{3}\.[0-9]{3})(MWST|TVA|IVA)$') - - def check_vat_ch(self, vat): - ''' - Check Switzerland VAT number. - ''' - # VAT number in Switzerland will change between 2011 and 2013 - # http://www.estv.admin.ch/mwst/themen/00154/00589/01107/index.html?lang=fr - # Old format is "TVA 123456" we will admit the user has to enter ch before the number - # Format will becomes such as "CHE-999.999.99C TVA" - # Both old and new format will be accepted till end of 2013 - # Accepted format are: (spaces are ignored) - # CH TVA ###### - # CH IVA ###### - # CH MWST ####### - # - # CHE#########MWST - # CHE#########TVA - # CHE#########IVA - # CHE-###.###.### MWST - # CHE-###.###.### TVA - # CHE-###.###.### IVA - # - if self.__check_vat_ch_re1.match(vat): - return True - match = self.__check_vat_ch_re2.match(vat) - if match: - # For new TVA numbers, do a mod11 check - num = filter(lambda s: s.isdigit(), match.group(1)) # get the digits only - factor = (5,4,3,2,7,6,5,4) - csum = sum([int(num[i]) * factor[i] for i in range(8)]) - check = (11 - (csum % 11)) % 11 - return check == int(num[8]) - return False - - def _ie_check_char(self, vat): - vat = vat.zfill(8) - extra = 0 - if vat[7] not in ' W': - if vat[7].isalpha(): - extra = 9 * (ord(vat[7]) - 64) - else: - # invalid - return -1 - checksum = extra + sum((8-i) * int(x) for i, x in enumerate(vat[:7])) - return 'WABCDEFGHIJKLMNOPQRSTUV'[checksum % 23] - - def check_vat_ie(self, vat): - """ Temporary Ireland VAT validation to support the new format - introduced in January 2013 in Ireland, until upstream is fixed. - TODO: remove when fixed upstream""" - if len(vat) not in (8, 9) or not vat[2:7].isdigit(): - return False - if len(vat) == 8: - # Normalize pre-2013 numbers: final space or 'W' not significant - vat += ' ' - if vat[:7].isdigit(): - return vat[7] == self._ie_check_char(vat[:7] + vat[8]) - elif vat[1] in (string.ascii_uppercase + '+*'): - # Deprecated format - # See http://www.revenue.ie/en/online/third-party-reporting/reporting-payment-details/faqs.html#section3 - return vat[7] == self._ie_check_char(vat[2:7] + vat[0] + vat[8]) - return False - - # Mexican VAT verification, contributed by Vauxoo - # and Panos Christeas - __check_vat_mx_re = re.compile(r"(?P[A-Za-z\xd1\xf1&]{3,4})" \ - r"[ \-_]?" \ - r"(?P[0-9]{2})(?P[01][0-9])(?P[0-3][0-9])" \ - r"[ \-_]?" \ - r"(?P[A-Za-z0-9&\xd1\xf1]{3})$") - def check_vat_mx(self, vat): - ''' Mexican VAT verification - - Verificar RFC México - ''' - # we convert to 8-bit encoding, to help the regex parse only bytes - vat = ustr(vat).encode('iso8859-1') - m = self.__check_vat_mx_re.match(vat) - if not m: - #No valid format - return False - try: - ano = int(m.group('ano')) - if ano > 30: - ano = 1900 + ano - else: - ano = 2000 + ano - datetime.date(ano, int(m.group('mes')), int(m.group('dia'))) - except ValueError: - return False - - #Valid format and valid date - return True - - - # Norway VAT validation, contributed by Rolv Råen (adEgo) - def check_vat_no(self, vat): - ''' - Check Norway VAT number.See http://www.brreg.no/english/coordination/number.html - ''' - if len(vat) != 9: - return False - try: - int(vat) - except ValueError: - return False - - sum = (3 * int(vat[0])) + (2 * int(vat[1])) + \ - (7 * int(vat[2])) + (6 * int(vat[3])) + \ - (5 * int(vat[4])) + (4 * int(vat[5])) + \ - (3 * int(vat[6])) + (2 * int(vat[7])) - - check = 11 -(sum % 11) - if check == 11: - check = 0 - if check == 10: - # 10 is not a valid check digit for an organization number - return False - return check == int(vat[8]) - - # Peruvian VAT validation, contributed by Vauxoo - def check_vat_pe(self, vat): - - vat_type,vat = vat and len(vat)>=2 and (vat[0], vat[1:]) or (False, False) - - if vat_type and vat_type.upper() == 'D': - #DNI - return True - elif vat_type and vat_type.upper() == 'R': - #verify RUC - factor = '5432765432' - sum = 0 - dig_check = False - if len(vat) != 11: - return False - try: - int(vat) - except ValueError: - return False - - for f in range(0,10): - sum += int(factor[f]) * int(vat[f]) - - subtraction = 11 - (sum % 11) - if subtraction == 10: - dig_check = 0 - elif subtraction == 11: - dig_check = 1 - else: - dig_check = subtraction - - return int(vat[10]) == dig_check - else: - return False - -res_partner() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import logging +import string +import datetime +import re +_logger = logging.getLogger(__name__) +try: + import vatnumber +except ImportError: + _logger.warning('VAT validation partially unavailable because the `vatnumber` Python library cannot be found. Install it to support more countries, for example with `easy_install vatnumber`.') + vatnumber = None + +from openerp.osv import fields, osv +from openerp.tools.misc import ustr +from openerp.tools.translate import _ +_ref_vat = { + 'at': 'ATU12345675', + 'be': 'BE0477472701', + 'bg': 'BG1234567892', + 'ch': 'CHE-123.456.788 TVA or CH TVA 123456', + 'cy': 'CY12345678F', + 'cz': 'CZ12345679', + 'de': 'DE123456788', + 'dk': 'DK12345674', + 'ee': 'EE123456780', + 'el': 'EL12345670', + 'es': 'ESA12345674', + 'fi': 'FI12345671', + 'fr': 'FR32123456789', + 'gb': 'GB123456782', + 'gr': 'GR12345670', + 'hu': 'HU12345676', + 'hr': 'HR01234567896', + 'ie': 'IE1234567T', + 'it': 'IT12345670017', + 'lt': 'LT123456715', + 'lu': 'LU12345613', + 'lv': 'LV41234567891', + 'mt': 'MT12345634', + 'mx': 'MXABC123456T1B', + 'nl': 'NL123456782B90', + 'no': 'NO123456785', + 'pl': 'PL1234567883', + 'pt': 'PT123456789', + 'ro': 'RO1234567897', + 'se': 'SE123456789701', + 'si': 'SI12345679', + 'sk': 'SK0012345675' + } + +class res_partner(osv.osv): + _inherit = 'res.partner' + + def _split_vat(self, vat): + vat_country, vat_number = vat[:2].lower(), vat[2:].replace(' ', '') + return (vat_country, vat_number) + + def simple_vat_check(self, cr, uid, country_code, vat_number, context = None): + """ + Check the VAT number depending of the country. + http://sima-pc.com/nif.php + """ + if not ustr(country_code).encode('utf-8').isalpha(): + return False + check_func_name = 'check_vat_' + country_code + check_func = getattr(self, check_func_name, None) or getattr(vatnumber, check_func_name, None) + if not check_func: + res_country = self.pool.get('res.country') + return bool(res_country.search(cr, uid, [('code', '=ilike', country_code)], context=context)) + else: + return check_func(vat_number) + + def vies_vat_check(self, cr, uid, country_code, vat_number, context = None): + try: + return vatnumber.check_vies(country_code.upper() + vat_number) + except Exception: + return self.simple_vat_check(cr, uid, country_code, vat_number, context=context) + + def button_check_vat(self, cr, uid, ids, context = None): + if not self.check_vat(cr, uid, ids, context=context): + msg = self._construct_constraint_msg(cr, uid, ids, context=context) + raise osv.except_osv(_('Error!'), msg) + return True + + def check_vat(self, cr, uid, ids, context = None): + user_company = self.pool.get('res.users').browse(cr, uid, uid).company_id + if user_company.vat_check_vies: + check_func = self.vies_vat_check + else: + check_func = self.simple_vat_check + for partner in self.browse(cr, uid, ids, context=context): + if not partner.vat: + continue + vat_country, vat_number = self._split_vat(partner.vat) + if not check_func(cr, uid, vat_country, vat_number, context=context): + return False + + return True + + def vat_change(self, cr, uid, ids, value, context = None): + return {'value': {'vat_subjected': bool(value)}} + + _columns = {'vat_subjected': fields.boolean('VAT Legal Statement', help='Check this box if the partner is subjected to the VAT. It will be used for the VAT legal statement.')} + + def _commercial_fields(self, cr, uid, context = None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['vat_subjected'] + + def _construct_constraint_msg(self, cr, uid, ids, context = None): + + def default_vat_check(cn, vn): + return cn[0] in string.ascii_lowercase and cn[1] in string.ascii_lowercase + + vat_country, vat_number = self._split_vat(self.browse(cr, uid, ids)[0].vat) + vat_no = "'CC##' (CC=Country Code, ##=VAT Number)" + if default_vat_check(vat_country, vat_number): + vat_no = _ref_vat[vat_country] if vat_country in _ref_vat else vat_no + return '\n' + _('This VAT number does not seem to be valid.\nNote: the expected format is %s') % vat_no + + __check_vat_ch_re1 = re.compile('(MWST|TVA|IVA)[0-9]{6}$') + __check_vat_ch_re2 = re.compile('E([0-9]{9}|-[0-9]{3}\\.[0-9]{3}\\.[0-9]{3})(MWST|TVA|IVA)$') + + def check_vat_ch(self, vat): + """ + Check Switzerland VAT number. + """ + if self.__check_vat_ch_re1.match(vat): + return True + match = self.__check_vat_ch_re2.match(vat) + if match: + num = filter(lambda s: s.isdigit(), match.group(1)) + factor = (5, 4, 3, 2, 7, 6, 5, 4) + csum = sum([ int(num[i]) * factor[i] for i in range(8) ]) + check = (11 - csum % 11) % 11 + return check == int(num[8]) + return False + + __check_vat_mx_re = re.compile('(?P[A-Za-z\\xd1\\xf1&]{3,4})[ \\-_]?(?P[0-9]{2})(?P[01][0-9])(?P[0-3][0-9])[ \\-_]?(?P[A-Za-z0-9&\\xd1\\xf1]{3})$') + + def check_vat_mx(self, vat): + """ Mexican VAT verification + + Verificar RFC M\xc3\xa9xico + """ + vat = ustr(vat).encode('iso8859-1') + m = self.__check_vat_mx_re.match(vat) + if not m: + return False + try: + ano = int(m.group('ano')) + if ano > 30: + ano = 1900 + ano + else: + ano = 2000 + ano + datetime.date(ano, int(m.group('mes')), int(m.group('dia'))) + except ValueError: + return False + + return True + + def check_vat_no(self, vat): + """ + Check Norway VAT number.See http://www.brreg.no/english/coordination/number.html + """ + if len(vat) != 9: + return False + try: + int(vat) + except ValueError: + return False + + sum = 3 * int(vat[0]) + 2 * int(vat[1]) + 7 * int(vat[2]) + 6 * int(vat[3]) + 5 * int(vat[4]) + 4 * int(vat[5]) + 3 * int(vat[6]) + 2 * int(vat[7]) + check = 11 - sum % 11 + if check == 11: + check = 0 + if check == 10: + return False + return check == int(vat[8]) + + +res_partner() \ No newline at end of file diff --git a/addons/base_vat/images/1_partner_vat.jpeg b/addons/base_vat/images/1_partner_vat.jpeg new file mode 100644 index 0000000000000..3df5d990de0cb Binary files /dev/null and b/addons/base_vat/images/1_partner_vat.jpeg differ diff --git a/addons/base_vat/res_company.py b/addons/base_vat/res_company.py index 9959ce82833f4..ffc341836955d 100644 --- a/addons/base_vat/res_company.py +++ b/addons/base_vat/res_company.py @@ -1,32 +1,25 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (c) 2011 OpenERP S.A. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv - -class res_company_vat (osv.osv): - _inherit = 'res.company' - _columns = { - 'vat_check_vies': fields.boolean('VIES VAT Check', - help="If checked, Partners VAT numbers will be fully validated against EU's VIES service " - "rather than via a simple format validation (checksum)."), - } - - +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv + +class res_company_vat(osv.osv): + _inherit = 'res.company' + _columns = {'vat_check_vies': fields.boolean('VIES VAT Check', help="If checked, Partners VAT numbers will be fully validated against EU's VIES service rather than via a simple format validation (checksum).")} \ No newline at end of file diff --git a/addons/board/board.py b/addons/board/board.py index 2bd2804356a22..e23e112c3608f 100644 --- a/addons/board/board.py +++ b/addons/board/board.py @@ -1,179 +1,128 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# Copyright (C) 2010-2013 OpenERP s.a. (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from operator import itemgetter -from textwrap import dedent - -from openerp import tools, SUPERUSER_ID -from openerp.osv import fields, osv - -class board_board(osv.osv): - _name = 'board.board' - _description = "Board" - _auto = False - _columns = {} - - @tools.cache() - def list(self, cr, uid, context=None): - Actions = self.pool.get('ir.actions.act_window') - Menus = self.pool.get('ir.ui.menu') - IrValues = self.pool.get('ir.values') - - act_ids = Actions.search(cr, uid, [('res_model', '=', self._name)], context=context) - refs = ['%s,%s' % (Actions._name, act_id) for act_id in act_ids] - - # cannot search "action" field on menu (non stored function field without search_fnct) - irv_ids = IrValues.search(cr, uid, [ - ('model', '=', 'ir.ui.menu'), - ('key', '=', 'action'), - ('key2', '=', 'tree_but_open'), - ('value', 'in', refs), - ], context=context) - menu_ids = map(itemgetter('res_id'), IrValues.read(cr, uid, irv_ids, ['res_id'], context=context)) - menu_ids = Menus._filter_visible_menus(cr, uid, menu_ids, context=context) - menu_names = Menus.name_get(cr, uid, menu_ids, context=context) - return [dict(id=m[0], name=m[1]) for m in menu_names] - - def _clear_list_cache(self): - self.list.clear_cache(self) - - def create(self, cr, user, vals, context=None): - return 0 - - def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - """ - Overrides orm field_view_get. - @return: Dictionary of Fields, arch and toolbar. - """ - - res = {} - res = super(board_board, self).fields_view_get(cr, user, view_id, view_type, - context, toolbar=toolbar, submenu=submenu) - - CustView = self.pool.get('ir.ui.view.custom') - vids = CustView.search(cr, user, [('user_id', '=', user), ('ref_id', '=', view_id)], context=context) - if vids: - view_id = vids[0] - arch = CustView.browse(cr, user, view_id, context=context) - res['custom_view_id'] = view_id - res['arch'] = arch.arch - res['arch'] = self._arch_preprocessing(cr, user, res['arch'], context=context) - res['toolbar'] = {'print': [], 'action': [], 'relate': []} - return res - - def _arch_preprocessing(self, cr, user, arch, context=None): - from lxml import etree - def remove_unauthorized_children(node): - for child in node.iterchildren(): - if child.tag == 'action' and child.get('invisible'): - node.remove(child) - else: - child = remove_unauthorized_children(child) - return node - - def encode(s): - if isinstance(s, unicode): - return s.encode('utf8') - return s - - archnode = etree.fromstring(encode(arch)) - return etree.tostring(remove_unauthorized_children(archnode), pretty_print=True) - - -class board_create(osv.osv_memory): - - def board_create(self, cr, uid, ids, context=None): - assert len(ids) == 1 - this = self.browse(cr, uid, ids[0], context=context) - - view_arch = dedent(""" - - - - - - - """.strip() % (this.name,)) - - view_id = self.pool.get('ir.ui.view').create(cr, uid, { - 'name': this.name, - 'model': 'board.board', - 'priority': 16, - 'type': 'form', - 'arch': view_arch, - }, context=context) - - action_id = self.pool.get('ir.actions.act_window').create(cr, uid, { - 'name': this.name, - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'board.board', - 'usage': 'menu', - 'view_id': view_id, - 'help': dedent('''
    -

    - This dashboard is empty. -

    - To add the first report into this dashboard, go to any - menu, switch to list or graph view, and click 'Add to - Dashboard' in the extended search options. -

    - You can filter and group data before inserting into the - dashboard using the search options. -

    -
    - ''') - }, context=context) - - menu_id = self.pool.get('ir.ui.menu').create(cr, SUPERUSER_ID, { - 'name': this.name, - 'parent_id': this.menu_parent_id.id, - 'action': 'ir.actions.act_window,%s' % (action_id,) - }, context=context) - - self.pool.get('board.board')._clear_list_cache() - - return { - 'type': 'ir.actions.client', - 'tag': 'reload', - 'params': { - 'menu_id': menu_id - }, - } - - def _default_menu_parent_id(self, cr, uid, context=None): - _, menu_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'menu_reporting_dashboard') - return menu_id - - _name = "board.create" - _description = "Board Creation" - - _columns = { - 'name': fields.char('Board Name', size=64, required=True), - 'menu_parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True), - } - - _defaults = { - 'menu_parent_id': _default_menu_parent_id, - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from operator import itemgetter +from textwrap import dedent +from openerp import tools +from openerp.osv import fields, osv + +class board_board(osv.osv): + _name = 'board.board' + _description = 'Board' + _auto = False + _columns = {} + + @tools.cache() + def list(self, cr, uid, context = None): + Actions = self.pool.get('ir.actions.act_window') + Menus = self.pool.get('ir.ui.menu') + IrValues = self.pool.get('ir.values') + act_ids = Actions.search(cr, uid, [('res_model', '=', self._name)], context=context) + refs = [ '%s,%s' % (Actions._name, act_id) for act_id in act_ids ] + irv_ids = IrValues.search(cr, uid, [('model', '=', 'ir.ui.menu'), + ('key', '=', 'action'), + ('key2', '=', 'tree_but_open'), + ('value', 'in', refs)], context=context) + menu_ids = map(itemgetter('res_id'), IrValues.read(cr, uid, irv_ids, ['res_id'], context=context)) + menu_names = Menus.name_get(cr, uid, menu_ids, context=context) + return [ dict(id=m[0], name=m[1]) for m in menu_names ] + + def _clear_list_cache(self): + self.list.clear_cache(self) + + def create(self, cr, user, vals, context = None): + return 0 + + def fields_view_get(self, cr, user, view_id = None, view_type = 'form', context = None, toolbar = False, submenu = False): + """ + Overrides orm field_view_get. + @return: Dictionary of Fields, arch and toolbar. + """ + res = {} + res = super(board_board, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu) + CustView = self.pool.get('ir.ui.view.custom') + vids = CustView.search(cr, user, [('user_id', '=', user), ('ref_id', '=', view_id)], context=context) + if vids: + view_id = vids[0] + arch = CustView.browse(cr, user, view_id, context=context) + res['custom_view_id'] = view_id + res['arch'] = arch.arch + res['arch'] = self._arch_preprocessing(cr, user, res['arch'], context=context) + res['toolbar'] = {'print': [], + 'action': [], + 'relate': []} + return res + + def _arch_preprocessing(self, cr, user, arch, context = None): + from lxml import etree + + def remove_unauthorized_children(node): + for child in node.iterchildren(): + if child.tag == 'action' and child.get('invisible'): + node.remove(child) + else: + child = remove_unauthorized_children(child) + + return node + + def encode(s): + if isinstance(s, unicode): + return s.encode('utf8') + return s + + archnode = etree.fromstring(encode(arch)) + return etree.tostring(remove_unauthorized_children(archnode), pretty_print=True) + + +class board_create(osv.osv_memory): + + def board_create(self, cr, uid, ids, context = None): + raise len(ids) == 1 or AssertionError + this = self.browse(cr, uid, ids[0], context=context) + view_arch = dedent('\n
    \n \n \n \n \n
    \n '.strip() % (this.name,)) + view_id = self.pool.get('ir.ui.view').create(cr, uid, {'name': this.name, + 'model': 'board.board', + 'priority': 16, + 'type': 'form', + 'arch': view_arch}, context=context) + action_id = self.pool.get('ir.actions.act_window').create(cr, uid, {'name': this.name, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'board.board', + 'usage': 'menu', + 'view_id': view_id, + 'help': dedent('
    \n

    \n This dashboard is empty.\n

    \n To add the first report into this dashboard, go to any\n menu, switch to list or graph view, and click \'Add to\n Dashboard\' in the extended search options.\n

    \n You can filter and group data before inserting into the\n dashboard using the search options.\n

    \n
    \n ')}, context=context) + menu_id = self.pool.get('ir.ui.menu').create(cr, uid, {'name': this.name, + 'parent_id': this.menu_parent_id.id, + 'action': 'ir.actions.act_window,%s' % (action_id,)}, context=context) + self.pool.get('board.board')._clear_list_cache() + return {'type': 'ir.actions.client', + 'tag': 'reload', + 'params': {'menu_id': menu_id}} + + def _default_menu_parent_id(self, cr, uid, context = None): + _, menu_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'menu_reporting_dashboard') + return menu_id + + _name = 'board.create' + _description = 'Board Creation' + _columns = {'name': fields.char('Board Name', size=64, required=True), + 'menu_parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True)} + _defaults = {'menu_parent_id': _default_menu_parent_id} \ No newline at end of file diff --git a/addons/board/controllers.py b/addons/board/controllers.py index 3bdebe1ef265b..307eaf260f88d 100644 --- a/addons/board/controllers.py +++ b/addons/board/controllers.py @@ -1,41 +1,50 @@ -# -*- coding: utf-8 -*- -from xml.etree import ElementTree - -import openerp -from openerp.addons.web.controllers.main import load_actions_from_ir_values - -class Board(openerp.addons.web.http.Controller): - _cp_path = '/board' - - @openerp.addons.web.http.jsonrequest - def add_to_dashboard(self, req, menu_id, action_id, context_to_save, domain, view_mode, name=''): - # FIXME move this method to board.board model - dashboard_action = load_actions_from_ir_values( - req, 'action', 'tree_but_open', [('ir.ui.menu', menu_id)], False) - - if dashboard_action: - action = dashboard_action[0][2] - if action['res_model'] == 'board.board' and action['views'][0][1] == 'form': - # Maybe should check the content instead of model board.board ? - view_id = action['views'][0][0] - board = req.session.model(action['res_model']).fields_view_get(view_id, 'form') - if board and 'arch' in board: - xml = ElementTree.fromstring(board['arch']) - column = xml.find('./board/column') - if column is not None: - new_action = ElementTree.Element('action', { - 'name': str(action_id), - 'string': name, - 'view_mode': view_mode, - 'context': str(context_to_save), - 'domain': str(domain) - }) - column.insert(0, new_action) - arch = ElementTree.tostring(xml, 'utf-8') - return req.session.model('ir.ui.view.custom').create({ - 'user_id': req.session._uid, - 'ref_id': view_id, - 'arch': arch - }, req.context) - - return False +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from xml.etree import ElementTree +import openerp +from openerp.addons.web.controllers.main import load_actions_from_ir_values + +class Board(openerp.addons.web.http.Controller): + _cp_path = '/board' + + @openerp.addons.web.http.jsonrequest + def add_to_dashboard(self, req, menu_id, action_id, context_to_save, domain, view_mode, name = ''): + dashboard_action = load_actions_from_ir_values(req, 'action', 'tree_but_open', [('ir.ui.menu', menu_id)], False) + if dashboard_action: + action = dashboard_action[0][2] + if action['res_model'] == 'board.board' and action['views'][0][1] == 'form': + view_id = action['views'][0][0] + board = req.session.model(action['res_model']).fields_view_get(view_id, 'form') + if board and 'arch' in board: + xml = ElementTree.fromstring(board['arch']) + column = xml.find('./board/column') + if column is not None: + new_action = ElementTree.Element('action', {'name': str(action_id), + 'string': name, + 'view_mode': view_mode, + 'context': str(context_to_save), + 'domain': str(domain)}) + column.insert(0, new_action) + arch = ElementTree.tostring(xml, 'utf-8') + return req.session.model('ir.ui.view.custom').create({'user_id': req.session._uid, + 'ref_id': view_id, + 'arch': arch}, req.context) + return False \ No newline at end of file diff --git a/addons/board/images/1_dashboard_definition.jpeg b/addons/board/images/1_dashboard_definition.jpeg new file mode 100644 index 0000000000000..984afebb7da37 Binary files /dev/null and b/addons/board/images/1_dashboard_definition.jpeg differ diff --git a/addons/board/images/2_publish_note.jpeg b/addons/board/images/2_publish_note.jpeg new file mode 100644 index 0000000000000..950d3bddbf575 Binary files /dev/null and b/addons/board/images/2_publish_note.jpeg differ diff --git a/addons/board/images/3_admin_dashboard.jpeg b/addons/board/images/3_admin_dashboard.jpeg new file mode 100644 index 0000000000000..7b634c5a25a61 Binary files /dev/null and b/addons/board/images/3_admin_dashboard.jpeg differ diff --git a/addons/board/static/src/css/Makefile b/addons/board/static/src/css/Makefile new file mode 100644 index 0000000000000..0cb5ae70f4afd --- /dev/null +++ b/addons/board/static/src/css/Makefile @@ -0,0 +1,3 @@ +dashboard.css: dashboard.sass + sass --trace -t expanded dashboard.sass dashboard.css + diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index d2587c87add8f..65737ad234e4b 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -424,7 +424,7 @@ instance.web.SearchView.include({ add_common_inputs: function() { this._super(); var vm = this.getParent().getParent(); - if (vm.inner_action && vm.inner_action.id && vm.inner_action.views) { + if (vm.inner_action && vm.inner_action.views) { (new instance.board.AddToDashboard(this)); } } diff --git a/addons/claim_from_delivery/images/1_claim_link_delivery_order.jpeg b/addons/claim_from_delivery/images/1_claim_link_delivery_order.jpeg new file mode 100644 index 0000000000000..464f1f49f4b01 Binary files /dev/null and b/addons/claim_from_delivery/images/1_claim_link_delivery_order.jpeg differ diff --git a/addons/contacts/images/contacts.jpeg b/addons/contacts/images/contacts.jpeg new file mode 100644 index 0000000000000..4068011bb6079 Binary files /dev/null and b/addons/contacts/images/contacts.jpeg differ diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 02c13c7ec7553..53c5e38ae51bc 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -1,233 +1,189 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import base64 -import time -from lxml import etree -from openerp.osv import fields -from openerp.osv import osv -from openerp import tools -from openerp.tools.translate import _ - -MAX_LEVEL = 15 -AVAILABLE_STATES = [ - ('draft', 'New'), - ('cancel', 'Cancelled'), - ('open', 'In Progress'), - ('pending', 'Pending'), - ('done', 'Closed') -] - -AVAILABLE_PRIORITIES = [ - ('1', 'Highest'), - ('2', 'High'), - ('3', 'Normal'), - ('4', 'Low'), - ('5', 'Lowest'), -] - -class crm_case_channel(osv.osv): - _name = "crm.case.channel" - _description = "Channels" - _order = 'name' - _columns = { - 'name': fields.char('Channel Name', size=64, required=True), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: 1, - } - -class crm_case_stage(osv.osv): - """ Model for case stages. This models the main stages of a document - management flow. Main CRM objects (leads, opportunities, project - issues, ...) will now use only stages, instead of state and stages. - Stages are for example used to display the kanban view of records. - """ - _name = "crm.case.stage" - _description = "Stage of case" - _rec_name = 'name' - _order = "sequence" - - _columns = { - 'name': fields.char('Stage Name', size=64, required=True, translate=True), - 'sequence': fields.integer('Sequence', help="Used to order stages. Lower is better."), - 'probability': fields.float('Probability (%)', required=True, help="This percentage depicts the default/average probability of the Case for this stage to be a success"), - 'on_change': fields.boolean('Change Probability Automatically', help="Setting this stage will change the probability automatically on the opportunity."), - 'requirements': fields.text('Requirements'), - 'section_ids':fields.many2many('crm.case.section', 'section_stage_rel', 'stage_id', 'section_id', string='Sections', - help="Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams."), - 'state': fields.selection(AVAILABLE_STATES, 'Related Status', required=True, - help="The status of your document will automatically change regarding the selected stage. " \ - "For example, if a stage is related to the status 'Close', when your document reaches this stage, it is automatically closed."), - 'case_default': fields.boolean('Default to New Sales Team', - help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."), - 'fold': fields.boolean('Fold by Default', - help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."), - 'type': fields.selection([ ('lead','Lead'), - ('opportunity', 'Opportunity'), - ('both', 'Both')], - string='Type', size=16, required=True, - help="This field is used to distinguish stages related to Leads from stages related to Opportunities, or to specify stages available for both types."), - } - - _defaults = { - 'sequence': lambda *args: 1, - 'probability': lambda *args: 0.0, - 'state': 'open', - 'fold': False, - 'type': 'both', - 'case_default': True, - } - -class crm_case_section(osv.osv): - """ Model for sales teams. """ - _name = "crm.case.section" - _inherits = {'mail.alias': 'alias_id'} - _inherit = "mail.thread" - _description = "Sales Teams" - _order = "complete_name" - - def get_full_name(self, cr, uid, ids, field_name, arg, context=None): - return dict(self.name_get(cr, uid, ids, context=context)) - - _columns = { - 'name': fields.char('Sales Team', size=64, required=True, translate=True), - 'complete_name': fields.function(get_full_name, type='char', size=256, readonly=True, store=True), - 'code': fields.char('Code', size=8), - 'active': fields.boolean('Active', help="If the active field is set to "\ - "false, it will allow you to hide the sales team without removing it."), - 'change_responsible': fields.boolean('Reassign Escalated', help="When escalating to this team override the salesman with the team leader."), - 'user_id': fields.many2one('res.users', 'Team Leader'), - 'member_ids':fields.many2many('res.users', 'sale_member_rel', 'section_id', 'member_id', 'Team Members'), - 'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by OpenERP about cases in this sales team"), - 'parent_id': fields.many2one('crm.case.section', 'Parent Team'), - 'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Teams'), - 'resource_calendar_id': fields.many2one('resource.calendar', "Working Time", help="Used to compute open days"), - 'note': fields.text('Description'), - 'working_hours': fields.float('Working Hours', digits=(16,2 )), - 'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, - help="The email address associated with this team. New emails received will automatically " - "create new leads assigned to the team."), - } - - def _get_stage_common(self, cr, uid, context): - ids = self.pool.get('crm.case.stage').search(cr, uid, [('case_default','=',1)], context=context) - return ids - - _defaults = { - 'active': 1, - 'stage_ids': _get_stage_common, - 'alias_domain': False, # always hide alias during creation - } - - _sql_constraints = [ - ('code_uniq', 'unique (code)', 'The code of the sales team must be unique !') - ] - - _constraints = [ - (osv.osv._check_recursion, 'Error ! You cannot create recursive Sales team.', ['parent_id']) - ] - - def name_get(self, cr, uid, ids, context=None): - """Overrides orm name_get method""" - if not isinstance(ids, list) : - ids = [ids] - res = [] - if not ids: - return res - reads = self.read(cr, uid, ids, ['name', 'parent_id'], context) - - for record in reads: - name = record['name'] - if record['parent_id']: - name = record['parent_id'][1] + ' / ' + name - res.append((record['id'], name)) - return res - - def create(self, cr, uid, vals, context=None): - mail_alias = self.pool.get('mail.alias') - if not vals.get('alias_id'): - vals.pop('alias_name', None) # prevent errors during copy() - alias_id = mail_alias.create_unique_alias(cr, uid, - {'alias_name': vals['name']}, - model_name="crm.lead", - context=context) - vals['alias_id'] = alias_id - res = super(crm_case_section, self).create(cr, uid, vals, context) - mail_alias.write(cr, uid, [vals['alias_id']], {'alias_defaults': {'section_id': res, 'type':'lead'}}, context) - return res - - def unlink(self, cr, uid, ids, context=None): - # Cascade-delete mail aliases as well, as they should not exist without the sales team. - mail_alias = self.pool.get('mail.alias') - alias_ids = [team.alias_id.id for team in self.browse(cr, uid, ids, context=context) if team.alias_id ] - res = super(crm_case_section, self).unlink(cr, uid, ids, context=context) - mail_alias.unlink(cr, uid, alias_ids, context=context) - return res - -class crm_case_categ(osv.osv): - """ Category of Case """ - _name = "crm.case.categ" - _description = "Category of Case" - _columns = { - 'name': fields.char('Name', size=64, required=True, translate=True), - 'section_id': fields.many2one('crm.case.section', 'Sales Team'), - 'object_id': fields.many2one('ir.model', 'Object Name'), - } - def _find_object_id(self, cr, uid, context=None): - """Finds id for case object""" - context = context or {} - object_id = context.get('object_id', False) - ids = self.pool.get('ir.model').search(cr, uid, ['|',('id', '=', object_id),('model', '=', context.get('object_name', False))]) - return ids and ids[0] or False - _defaults = { - 'object_id' : _find_object_id - } - -class crm_case_resource_type(osv.osv): - """ Resource Type of case """ - _name = "crm.case.resource.type" - _description = "Campaign" - _rec_name = "name" - _columns = { - 'name': fields.char('Campaign Name', size=64, required=True, translate=True), - 'section_id': fields.many2one('crm.case.section', 'Sales Team'), - } - -def _links_get(self, cr, uid, context=None): - """Gets links value for reference field""" - obj = self.pool.get('res.request.link') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['object', 'name'], context) - return [(r['object'], r['name']) for r in res] - -class crm_payment_mode(osv.osv): - """ Payment Mode for Fund """ - _name = "crm.payment.mode" - _description = "CRM Payment Mode" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'section_id': fields.many2one('crm.case.section', 'Sales Team'), - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import base64 +import time +from lxml import etree +from openerp.osv import fields +from openerp.osv import osv +from openerp import tools +from openerp.tools.translate import _ +MAX_LEVEL = 15 +AVAILABLE_STATES = [('draft', 'New'), + ('cancel', 'Cancelled'), + ('open', 'In Progress'), + ('pending', 'Pending'), + ('done', 'Closed')] +AVAILABLE_PRIORITIES = [('1', 'Highest'), + ('2', 'High'), + ('3', 'Normal'), + ('4', 'Low'), + ('5', 'Lowest')] + +class crm_case_channel(osv.osv): + _name = 'crm.case.channel' + _description = 'Channels' + _order = 'name' + _columns = {'name': fields.char('Channel Name', size=64, required=True), + 'active': fields.boolean('Active')} + _defaults = {'active': lambda *a: 1} + + +class crm_case_stage(osv.osv): + """ Model for case stages. This models the main stages of a document + management flow. Main CRM objects (leads, opportunities, project + issues, ...) will now use only stages, instead of state and stages. + Stages are for example used to display the kanban view of records. + """ + _name = 'crm.case.stage' + _description = 'Stage of case' + _rec_name = 'name' + _order = 'sequence' + _columns = {'name': fields.char('Stage Name', size=64, required=True, translate=True), + 'sequence': fields.integer('Sequence', help='Used to order stages. Lower is better.'), + 'probability': fields.float('Probability (%)', required=True, help='This percentage depicts the default/average probability of the Case for this stage to be a success'), + 'on_change': fields.boolean('Change Probability Automatically', help='Setting this stage will change the probability automatically on the opportunity.'), + 'requirements': fields.text('Requirements'), + 'section_ids': fields.many2many('crm.case.section', 'section_stage_rel', 'stage_id', 'section_id', string='Sections', help='Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams.'), + 'state': fields.selection(AVAILABLE_STATES, 'Related Status', required=True, help="The status of your document will automatically change regarding the selected stage. For example, if a stage is related to the status 'Close', when your document reaches this stage, it is automatically closed."), + 'case_default': fields.boolean('Default to New Sales Team', help='If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams.'), + 'fold': fields.boolean('Fold by Default', help='This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display.'), + 'type': fields.selection([('lead', 'Lead'), ('opportunity', 'Opportunity'), ('both', 'Both')], string='Type', size=16, required=True, help='This field is used to distinguish stages related to Leads from stages related to Opportunities, or to specify stages available for both types.')} + _defaults = {'sequence': lambda *args: 1, + 'probability': lambda *args: 0.0, + 'state': 'open', + 'fold': False, + 'type': 'both', + 'case_default': True} + + +class crm_case_section(osv.osv): + """ Model for sales teams. """ + _name = 'crm.case.section' + _inherits = {'mail.alias': 'alias_id'} + _inherit = 'mail.thread' + _description = 'Sales Teams' + _order = 'complete_name' + + def get_full_name(self, cr, uid, ids, field_name, arg, context = None): + return dict(self.name_get(cr, uid, ids, context=context)) + + _columns = {'name': fields.char('Sales Team', size=64, required=True, translate=True), + 'complete_name': fields.function(get_full_name, type='char', size=256, readonly=True, store=True), + 'code': fields.char('Code', size=8), + 'active': fields.boolean('Active', help='If the active field is set to true, it will allow you to hide the sales team without removing it.'), + 'change_responsible': fields.boolean('Reassign Escalated', help='When escalating to this team override the salesman with the team leader.'), + 'user_id': fields.many2one('res.users', 'Team Leader'), + 'member_ids': fields.many2many('res.users', 'sale_member_rel', 'section_id', 'member_id', 'Team Members'), + 'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by OpenERP about cases in this sales team"), + 'parent_id': fields.many2one('crm.case.section', 'Parent Team'), + 'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Teams'), + 'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help='Used to compute open days'), + 'note': fields.text('Description'), + 'working_hours': fields.float('Working Hours', digits=(16, 2)), + 'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'), + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete='restrict', required=True, help='The email address associated with this team. New emails received will automatically create new leads assigned to the team.')} + + def _get_stage_common(self, cr, uid, context): + ids = self.pool.get('crm.case.stage').search(cr, uid, [('case_default', '=', 1)], context=context) + return ids + + _defaults = {'active': 1, + 'stage_ids': _get_stage_common, + 'alias_domain': False} + _sql_constraints = [('code_uniq', 'unique (code)', 'The code of the sales team must be unique !')] + _constraints = [(osv.osv._check_recursion, 'Error ! You cannot create recursive Sales team.', ['parent_id'])] + + def name_get(self, cr, uid, ids, context = None): + """Overrides orm name_get method""" + if not isinstance(ids, list): + ids = [ids] + res = [] + if not ids: + return res + reads = self.read(cr, uid, ids, ['name', 'parent_id'], context) + for record in reads: + name = record['name'] + if record['parent_id']: + name = record['parent_id'][1] + ' / ' + name + res.append((record['id'], name)) + + return res + + def create(self, cr, uid, vals, context = None): + mail_alias = self.pool.get('mail.alias') + if not vals.get('alias_id'): + vals.pop('alias_name', None) + alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': vals['name']}, model_name='crm.lead', context=context) + vals['alias_id'] = alias_id + res = super(crm_case_section, self).create(cr, uid, vals, context) + mail_alias.write(cr, uid, [vals['alias_id']], {'alias_defaults': {'section_id': res, + 'type': 'lead'}}, context) + return res + + def unlink(self, cr, uid, ids, context = None): + mail_alias = self.pool.get('mail.alias') + alias_ids = [ team.alias_id.id for team in self.browse(cr, uid, ids, context=context) if team.alias_id ] + res = super(crm_case_section, self).unlink(cr, uid, ids, context=context) + mail_alias.unlink(cr, uid, alias_ids, context=context) + return res + + +class crm_case_categ(osv.osv): + """ Category of Case """ + _name = 'crm.case.categ' + _description = 'Category of Case' + _columns = {'name': fields.char('Name', size=64, required=True, translate=True), + 'section_id': fields.many2one('crm.case.section', 'Sales Team'), + 'object_id': fields.many2one('ir.model', 'Object Name')} + + def _find_object_id(self, cr, uid, context = None): + """Finds id for case object""" + context = context or {} + object_id = context.get('object_id', False) + ids = self.pool.get('ir.model').search(cr, uid, ['|', ('id', '=', object_id), ('model', '=', context.get('object_name', False))]) + return ids and ids[0] or False + + _defaults = {'object_id': _find_object_id} + + +class crm_case_resource_type(osv.osv): + """ Resource Type of case """ + _name = 'crm.case.resource.type' + _description = 'Campaign' + _rec_name = 'name' + _columns = {'name': fields.char('Campaign Name', size=64, required=True, translate=True), + 'section_id': fields.many2one('crm.case.section', 'Sales Team')} + + +def _links_get(self, cr, uid, context = None): + """Gets links value for reference field""" + obj = self.pool.get('res.request.link') + ids = obj.search(cr, uid, []) + res = obj.read(cr, uid, ids, ['object', 'name'], context) + return [ (r['object'], r['name']) for r in res ] + + +class crm_payment_mode(osv.osv): + """ Payment Mode for Fund """ + _name = 'crm.payment.mode' + _description = 'CRM Payment Mode' + _columns = {'name': fields.char('Name', size=64, required=True), + 'section_id': fields.many2one('crm.case.section', 'Sales Team')} \ No newline at end of file diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index e272eca1d2f77..d27589dd5450c 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -1,1106 +1,946 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.addons.base_status.base_stage import base_stage -import crm -from datetime import datetime -from operator import itemgetter -from openerp.osv import fields, osv, orm -import time -from openerp import SUPERUSER_ID -from openerp import tools -from openerp.tools.translate import _ -from openerp.tools import html2plaintext - -from base.res.res_partner import format_address - -CRM_LEAD_FIELDS_TO_MERGE = ['name', - 'partner_id', - 'channel_id', - 'company_id', - 'country_id', - 'section_id', - 'state_id', - 'stage_id', - 'type_id', - 'user_id', - 'title', - 'city', - 'contact_name', - 'description', - 'email', - 'fax', - 'mobile', - 'partner_name', - 'phone', - 'probability', - 'planned_revenue', - 'street', - 'street2', - 'zip', - 'create_date', - 'date_action_last', - 'date_action_next', - 'email_from', - 'email_cc', - 'partner_name'] -CRM_LEAD_PENDING_STATES = ( - crm.AVAILABLE_STATES[2][0], # Cancelled - crm.AVAILABLE_STATES[3][0], # Done - crm.AVAILABLE_STATES[4][0], # Pending -) - -class crm_lead(base_stage, format_address, osv.osv): - """ CRM Lead Case """ - _name = "crm.lead" - _description = "Lead/Opportunity" - _order = "priority,date_action,id desc" - _inherit = ['mail.thread', 'ir.needaction_mixin'] - - _track = { - 'state': { - 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['new', 'draft'], - 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', - 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel', - }, - 'stage_id': { - 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'draft', 'cancel', 'done'], - }, - } - - def create(self, cr, uid, vals, context=None): - if context is None: - context = {} - if vals.get('type') and not context.get('default_type'): - context['default_type'] = vals.get('type') - if vals.get('section_id') and not context.get('default_section_id'): - context['default_section_id'] = vals.get('section_id') - - # context: no_log, because subtype already handle this - create_context = dict(context, mail_create_nolog=True) - return super(crm_lead, self).create(cr, uid, vals, context=create_context) - - def _get_default_section_id(self, cr, uid, context=None): - """ Gives default section by checking if present in the context """ - return self._resolve_section_id_from_context(cr, uid, context=context) or False - - def _get_default_stage_id(self, cr, uid, context=None): - """ Gives default stage_id """ - section_id = self._get_default_section_id(cr, uid, context=context) - return self.stage_find(cr, uid, [], section_id, [('state', '=', 'draft')], context=context) - - def _resolve_section_id_from_context(self, cr, uid, context=None): - """ Returns ID of section based on the value of 'section_id' - context key, or None if it cannot be resolved to a single - Sales Team. - """ - if context is None: - context = {} - if type(context.get('default_section_id')) in (int, long): - return context.get('default_section_id') - if isinstance(context.get('default_section_id'), basestring): - section_name = context['default_section_id'] - section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=section_name, context=context) - if len(section_ids) == 1: - return int(section_ids[0][0]) - return None - - def _resolve_type_from_context(self, cr, uid, context=None): - """ Returns the type (lead or opportunity) from the type context - key. Returns None if it cannot be resolved. - """ - if context is None: - context = {} - return context.get('default_type') - - def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): - access_rights_uid = access_rights_uid or uid - stage_obj = self.pool.get('crm.case.stage') - order = stage_obj._order - # lame hack to allow reverting search, should just work in the trivial case - if read_group_order == 'stage_id desc': - order = "%s desc" % order - # retrieve section_id from the context and write the domain - # - ('id', 'in', 'ids'): add columns that should be present - # - OR ('case_default', '=', True), ('fold', '=', False): add default columns that are not folded - # - OR ('section_ids', '=', section_id), ('fold', '=', False) if section_id: add section columns that are not folded - search_domain = [] - section_id = self._resolve_section_id_from_context(cr, uid, context=context) - if section_id: - search_domain += ['|', ('section_ids', '=', section_id)] - search_domain += [('id', 'in', ids)] - else: - search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)] - # retrieve type from the context (if set: choose 'type' or 'both') - type = self._resolve_type_from_context(cr, uid, context=context) - if type: - search_domain += ['|', ('type', '=', type), ('type', '=', 'both')] - # perform search - stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) - result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) - # restore order of the search - result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) - - fold = {} - for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): - fold[stage.id] = stage.fold or False - return result, fold - - def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - if view_type == 'form' and context and context.get('opportunity_id'): - # TODO: replace by get_formview_action call - lead_type = self.browse(cr, user, context['opportunity_id'], context=context).type - view_lead_xml_id = 'crm_case_form_view_oppor' if lead_type == 'opportunity' else 'crm_case_form_view_leads' - _, view_id = self.pool['ir.model.data'].get_object_reference(cr, user, 'crm', view_lead_xml_id) - res = super(crm_lead,self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu) - if view_type == 'form': - res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context) - return res - - _group_by_full = { - 'stage_id': _read_group_stage_ids - } - - def _compute_day(self, cr, uid, ids, fields, args, context=None): - """ - :return dict: difference between current date and log date - """ - cal_obj = self.pool.get('resource.calendar') - res_obj = self.pool.get('resource.resource') - - res = {} - for lead in self.browse(cr, uid, ids, context=context): - for field in fields: - res[lead.id] = {} - duration = 0 - ans = False - if field == 'day_open': - if lead.date_open: - date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") - date_open = datetime.strptime(lead.date_open, "%Y-%m-%d %H:%M:%S") - ans = date_open - date_create - date_until = lead.date_open - elif field == 'day_close': - if lead.date_closed: - date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") - date_close = datetime.strptime(lead.date_closed, "%Y-%m-%d %H:%M:%S") - date_until = lead.date_closed - ans = date_close - date_create - if ans: - resource_id = False - if lead.user_id: - resource_ids = res_obj.search(cr, uid, [('user_id','=',lead.user_id.id)]) - if len(resource_ids): - resource_id = resource_ids[0] - - duration = float(ans.days) - if lead.section_id and lead.section_id.resource_calendar_id: - duration = float(ans.days) * 24 - new_dates = cal_obj.interval_get(cr, - uid, - lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False, - datetime.strptime(lead.create_date, '%Y-%m-%d %H:%M:%S'), - duration, - resource=resource_id - ) - no_days = [] - date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S') - for in_time, out_time in new_dates: - if in_time.date not in no_days: - no_days.append(in_time.date) - if out_time > date_until: - break - duration = len(no_days) - res[lead.id][field] = abs(int(duration)) - return res - - def _history_search(self, cr, uid, obj, name, args, context=None): - res = [] - msg_obj = self.pool.get('mail.message') - message_ids = msg_obj.search(cr, uid, [('email_from','!=',False), ('subject', args[0][1], args[0][2])], context=context) - lead_ids = self.search(cr, uid, [('message_ids', 'in', message_ids)], context=context) - - if lead_ids: - return [('id', 'in', lead_ids)] - else: - return [('id', '=', '0')] - - _columns = { - 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', track_visibility='onchange', - select=True, help="Linked partner (optional). Usually created when converting the lead."), - - 'id': fields.integer('ID', readonly=True), - 'name': fields.char('Subject', size=64, required=True, select=1), - 'active': fields.boolean('Active', required=False), - 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), - 'email_from': fields.char('Email', size=128, help="Email address of the contact", select=1), - 'section_id': fields.many2one('crm.case.section', 'Sales Team', - select=True, track_visibility='onchange', help='When sending mails, the default email address is taken from the sales team.'), - 'create_date': fields.datetime('Creation Date' , readonly=True), - 'email_cc': fields.text('Global CC', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), - 'description': fields.text('Notes'), - 'write_date': fields.datetime('Update Date' , readonly=True), - 'categ_ids': fields.many2many('crm.case.categ', 'crm_lead_category_rel', 'lead_id', 'category_id', 'Categories', \ - domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]"), - 'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \ - domain="['|',('section_id','=',section_id),('section_id','=',False)]", help="From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?"), - 'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"), - 'contact_name': fields.char('Contact Name', size=64), - 'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1), - 'opt_out': fields.boolean('Opt-Out', oldname='optout', - help="If opt-out is checked, this contact has refused to receive emails for mass mailing and marketing campaign. " - "Filter 'Available for Mass Mailing' allows users to filter the leads when performing mass mailing."), - 'type': fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', select=True, help="Type is used to separate Leads and Opportunities"), - 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), - 'date_closed': fields.datetime('Closed', readonly=True), - 'stage_id': fields.many2one('crm.case.stage', 'Stage', track_visibility='onchange', select=True, - domain="['&', '&', ('fold', '=', False), ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"), - 'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'), - 'referred': fields.char('Referred By', size=64), - 'date_open': fields.datetime('Opened', readonly=True), - 'day_open': fields.function(_compute_day, string='Days to Open', \ - multi='day_open', type="float", store=True), - 'day_close': fields.function(_compute_day, string='Days to Close', \ - multi='day_close', type="float", store=True), - 'state': fields.related('stage_id', 'state', type="selection", store=True, - selection=crm.AVAILABLE_STATES, string="Status", readonly=True, select=True, - help='The Status is set to \'Draft\', when a case is created. If the case is in progress the Status is set to \'Open\'. When the case is over, the Status is set to \'Done\'. If the case needs to be reviewed then the Status is set to \'Pending\'.'), - - # Only used for type opportunity - 'probability': fields.float('Success Rate (%)',group_operator="avg"), - 'planned_revenue': fields.float('Expected Revenue', track_visibility='always'), - 'ref': fields.reference('Reference', selection=crm._links_get, size=128), - 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), - 'phone': fields.char("Phone", size=64), - 'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."), - 'date_action': fields.date('Next Action Date', select=True), - 'title_action': fields.char('Next Action', size=64), - 'color': fields.integer('Color Index'), - 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), - 'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True), - 'company_currency': fields.related('company_id', 'currency_id', type='many2one', string='Currency', readonly=True, relation="res.currency"), - 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), - 'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True), - - # Fields for address, due to separation from crm and res.partner - 'street': fields.char('Street', size=128), - 'street2': fields.char('Street2', size=128), - 'zip': fields.char('Zip', change_default=True, size=24), - 'city': fields.char('City', size=128), - 'state_id': fields.many2one("res.country.state", 'State'), - 'country_id': fields.many2one('res.country', 'Country'), - 'phone': fields.char('Phone', size=64), - 'fax': fields.char('Fax', size=64), - 'mobile': fields.char('Mobile', size=64), - 'function': fields.char('Function', size=128), - 'title': fields.many2one('res.partner.title', 'Title'), - 'company_id': fields.many2one('res.company', 'Company', select=1), - 'payment_mode': fields.many2one('crm.payment.mode', 'Payment Mode', \ - domain="[('section_id','=',section_id)]"), - 'planned_cost': fields.float('Planned Costs'), - } - - _defaults = { - 'active': 1, - 'type': 'lead', - 'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c), - 'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c), - 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), - 'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c), - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c), - 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], - 'color': 0, - } - - _sql_constraints = [ - ('check_probability', 'check(probability >= 0 and probability <= 100)', 'The probability of closing the deal should be between 0% and 100%!') - ] - - def onchange_stage_id(self, cr, uid, ids, stage_id, context=None): - if not stage_id: - return {'value':{}} - stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context) - if not stage.on_change: - return {'value':{}} - return {'value':{'probability': stage.probability}} - - def on_change_partner(self, cr, uid, ids, partner_id, context=None): - values = {} - if partner_id: - partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) - values = { - 'partner_name' : partner.name, - 'title': partner.title and partner.title.id or False, - 'street' : partner.street, - 'street2' : partner.street2, - 'city' : partner.city, - 'state_id' : partner.state_id and partner.state_id.id or False, - 'country_id' : partner.country_id and partner.country_id.id or False, - 'email_from' : partner.email, - 'phone' : partner.phone, - 'mobile' : partner.mobile, - 'fax' : partner.fax, - 'zip': partner.zip, - 'function': partner.function, - } - return {'value' : values} - - def on_change_user(self, cr, uid, ids, user_id, context=None): - """ When changing the user, also set a section_id or restrict section id - to the ones user_id is member of. """ - if user_id: - section_ids = self.pool.get('crm.case.section').search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)], context=context) - if section_ids: - return {'value': {'section_id': section_ids[0]}} - return {'value': {}} - - def _check(self, cr, uid, ids=False, context=None): - """ Override of the base.stage method. - Function called by the scheduler to process cases for date actions - Only works on not done and cancelled cases - """ - cr.execute('select * from crm_case \ - where (date_action_last<%s or date_action_last is null) \ - and (date_action_next<=%s or date_action_next is null) \ - and state not in (\'cancel\',\'done\')', - (time.strftime("%Y-%m-%d %H:%M:%S"), - time.strftime('%Y-%m-%d %H:%M:%S'))) - - ids2 = map(lambda x: x[0], cr.fetchall() or []) - cases = self.browse(cr, uid, ids2, context=context) - return self._action(cr, uid, cases, False, context=context) - - def stage_find(self, cr, uid, cases, section_id, domain=None, order='sequence', context=None): - """ Override of the base.stage method - Parameter of the stage search taken from the lead: - - type: stage type must be the same or 'both' - - section_id: if set, stages must belong to this section or - be a default stage; if not set, stages must be default - stages - """ - if isinstance(cases, (int, long)): - cases = self.browse(cr, uid, cases, context=context) - # collect all section_ids - section_ids = [] - types = ['both'] - if not cases : - type = context.get('default_type') - types += [type] - if section_id: - section_ids.append(section_id) - for lead in cases: - if lead.section_id: - section_ids.append(lead.section_id.id) - if lead.type not in types: - types.append(lead.type) - # OR all section_ids and OR with case_default - search_domain = [] - if section_ids: - search_domain += [('|')] * len(section_ids) - for section_id in section_ids: - search_domain.append(('section_ids', '=', section_id)) - else: - search_domain.append(('case_default', '=', True)) - # AND with cases types - search_domain.append(('type', 'in', types)) - # AND with the domain in parameter - search_domain += list(domain) - # perform search, return the first found - stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, context=context) - if stage_ids: - return stage_ids[0] - return False - - def case_cancel(self, cr, uid, ids, context=None): - """ Overrides case_cancel from base_stage to set probability """ - res = super(crm_lead, self).case_cancel(cr, uid, ids, context=context) - self.write(cr, uid, ids, {'probability' : 0.0}, context=context) - return res - - def case_reset(self, cr, uid, ids, context=None): - """ Overrides case_reset from base_stage to set probability """ - res = super(crm_lead, self).case_reset(cr, uid, ids, context=context) - self.write(cr, uid, ids, {'probability': 0.0}, context=context) - return res - - def case_mark_lost(self, cr, uid, ids, context=None): - """ Mark the case as lost: state=cancel and probability=0 """ - for lead in self.browse(cr, uid, ids): - stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0),('on_change','=',True)], context=context) - if stage_id: - self.case_set(cr, uid, [lead.id], values_to_update={'probability': 0.0, 'date_closed': fields.datetime.now()}, new_stage_id=stage_id, context=context) - return True - - def case_mark_won(self, cr, uid, ids, context=None): - """ Mark the case as won: state=done and probability=100 """ - for lead in self.browse(cr, uid, ids): - stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0),('on_change','=',True)], context=context) - if stage_id: - self.case_set(cr, uid, [lead.id], values_to_update={'probability': 100.0, 'date_closed': fields.datetime.now()}, new_stage_id=stage_id, context=context) - return True - - def set_priority(self, cr, uid, ids, priority): - """ Set lead priority - """ - return self.write(cr, uid, ids, {'priority' : priority}) - - def set_high_priority(self, cr, uid, ids, context=None): - """ Set lead priority to high - """ - return self.set_priority(cr, uid, ids, '1') - - def set_normal_priority(self, cr, uid, ids, context=None): - """ Set lead priority to normal - """ - return self.set_priority(cr, uid, ids, '3') - - def _merge_get_result_type(self, cr, uid, opps, context=None): - """ - Define the type of the result of the merge. If at least one of the - element to merge is an opp, the resulting new element will be an opp. - Otherwise it will be a lead. - - We'll directly use a list of browse records instead of a list of ids - for performances' sake: it will spare a second browse of the - leads/opps. - - :param list opps: list of browse records containing the leads/opps to process - :return string type: the type of the final element - """ - for opp in opps: - if (opp.type == 'opportunity'): - return 'opportunity' - - return 'lead' - - def _merge_data(self, cr, uid, ids, oldest, fields, context=None): - """ - Prepare lead/opp data into a dictionary for merging. Different types - of fields are processed in different ways: - - text: all the values are concatenated - - m2m and o2m: those fields aren't processed - - m2o: the first not null value prevails (the other are dropped) - - any other type of field: same as m2o - - :param list ids: list of ids of the leads to process - :param list fields: list of leads' fields to process - :return dict data: contains the merged values - """ - opportunities = self.browse(cr, uid, ids, context=context) - - def _get_first_not_null(attr): - for opp in opportunities: - if hasattr(opp, attr) and bool(getattr(opp, attr)): - return getattr(opp, attr) - return False - - def _get_first_not_null_id(attr): - res = _get_first_not_null(attr) - return res and res.id or False - - def _concat_all(attr): - return '\n\n'.join(filter(lambda x: x, [getattr(opp, attr) or '' for opp in opportunities if hasattr(opp, attr)])) - - # Process the fields' values - data = {} - for field_name in fields: - field_info = self._all_columns.get(field_name) - if field_info is None: - continue - field = field_info.column - if field._type in ('many2many', 'one2many'): - continue - elif field._type == 'many2one': - data[field_name] = _get_first_not_null_id(field_name) # !! - elif field._type == 'text': - data[field_name] = _concat_all(field_name) #not lost - else: - data[field_name] = _get_first_not_null(field_name) #not lost - - # Define the resulting type ('lead' or 'opportunity') - data['type'] = self._merge_get_result_type(cr, uid, opportunities, context) - return data - - def _mail_body(self, cr, uid, lead, fields, title=False, context=None): - body = [] - if title: - body.append("%s\n" % (title)) - - for field_name in fields: - field_info = self._all_columns.get(field_name) - if field_info is None: - continue - field = field_info.column - value = '' - - if field._type == 'selection': - if hasattr(field.selection, '__call__'): - key = field.selection(self, cr, uid, context=context) - else: - key = field.selection - value = dict(key).get(lead[field_name], lead[field_name]) - elif field._type == 'many2one': - if lead[field_name]: - value = lead[field_name].name_get()[0][1] - elif field._type == 'many2many': - if lead[field_name]: - for val in lead[field_name]: - field_value = val.name_get()[0][1] - value += field_value + "," - else: - value = lead[field_name] - - body.append("%s: %s" % (field.string, value or '')) - return "
    ".join(body + ['
    ']) - - def _merge_notify(self, cr, uid, opportunity_id, opportunities, context=None): - """ - Create a message gathering merged leads/opps information. - """ - #TOFIX: mail template should be used instead of fix body, subject text - details = [] - result_type = self._merge_get_result_type(cr, uid, opportunities, context) - if result_type == 'lead': - merge_message = _('Merged leads') - else: - merge_message = _('Merged opportunities') - subject = [merge_message] - for opportunity in opportunities: - subject.append(opportunity.name) - title = "%s : %s" % (opportunity.type == 'opportunity' and _('Merged opportunity') or _('Merged lead'), opportunity.name) - fields = list(CRM_LEAD_FIELDS_TO_MERGE) - details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context)) - - # Chatter message's subject - subject = subject[0] + ": " + ", ".join(subject[1:]) - details = "\n\n".join(details) - return self.message_post(cr, uid, [opportunity_id], body=details, subject=subject, context=context) - - def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context=None): - message = self.pool.get('mail.message') - for opportunity in opportunities: - for history in opportunity.message_ids: - message.write(cr, uid, history.id, { - 'res_id': opportunity_id, - 'subject' : _("From %s : %s") % (opportunity.name, history.subject) - }, context=context) - - return True - - def _merge_opportunity_attachments(self, cr, uid, opportunity_id, opportunities, context=None): - attach_obj = self.pool.get('ir.attachment') - - # return attachments of opportunity - def _get_attachments(opportunity_id): - attachment_ids = attach_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context) - return attach_obj.browse(cr, uid, attachment_ids, context=context) - - first_attachments = _get_attachments(opportunity_id) - #counter of all attachments to move. Used to make sure the name is different for all attachments - count = 1 - for opportunity in opportunities: - attachments = _get_attachments(opportunity.id) - for attachment in attachments: - values = {'res_id': opportunity_id,} - for attachment_in_first in first_attachments: - if attachment.name == attachment_in_first.name: - name = "%s (%s)" % (attachment.name, count,), - count+=1 - attachment.write(values) - return True - - def _merge_opportunity_phonecalls(self, cr, uid, opportunity_id, opportunities, context=None): - phonecall_obj = self.pool['crm.phonecall'] - for opportunity in opportunities: - for phonecall_id in phonecall_obj.search(cr, uid, [('opportunity_id', '=', opportunity.id)], context=context): - phonecall_obj.write(cr, uid, phonecall_id, {'opportunity_id': opportunity_id}, context=context) - return True - - def merge_opportunity(self, cr, uid, ids, context=None): - """ - Different cases of merge: - - merge leads together = 1 new lead - - merge at least 1 opp with anything else (lead or opp) = 1 new opp - - :param list ids: leads/opportunities ids to merge - :return int id: id of the resulting lead/opp - """ - if context is None: - context = {} - - if len(ids) <= 1: - raise osv.except_osv(_('Warning!'), _('Please select more than one element (lead or opportunity) from the list view.')) - - opportunities = self.browse(cr, uid, ids, context=context) - sequenced_opps = [] - for opportunity in opportunities: - sequence = -1 - if opportunity.stage_id and opportunity.stage_id.state != 'cancel': - sequence = opportunity.stage_id.sequence - sequenced_opps.append(((int(sequence != -1 and opportunity.type == 'opportunity'), sequence, -opportunity.id), opportunity)) - - sequenced_opps.sort(reverse=True) - opportunities = map(itemgetter(1), sequenced_opps) - ids = [opportunity.id for opportunity in opportunities] - highest = opportunities[0] - opportunities_rest = opportunities[1:] - - tail_opportunities = opportunities_rest - - fields = list(CRM_LEAD_FIELDS_TO_MERGE) - merged_data = self._merge_data(cr, uid, ids, highest, fields, context=context) - - # Merge messages and attachements into the first opportunity - self._merge_opportunity_history(cr, uid, highest.id, tail_opportunities, context=context) - self._merge_opportunity_attachments(cr, uid, highest.id, tail_opportunities, context=context) - self._merge_opportunity_phonecalls(cr, uid, highest.id, tail_opportunities, context=context) - - # Merge notifications about loss of information - opportunities = [highest] - opportunities.extend(opportunities_rest) - self._merge_notify(cr, uid, highest.id, opportunities, context=context) - # Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence - if merged_data.get('section_id'): - section_stage_ids = self.pool.get('crm.case.stage').search(cr, uid, [('section_ids', 'in', merged_data['section_id']), ('type', '=', merged_data.get('type'))], order='sequence', context=context) - if merged_data.get('stage_id') not in section_stage_ids: - merged_data['stage_id'] = section_stage_ids and section_stage_ids[0] or False - # Write merged data into first opportunity - self.write(cr, uid, [highest.id], merged_data, context=context) - # Delete tail opportunities - # We use the SUPERUSER to avoid access rights issues because as the user had the rights to see the records it should be safe to do so - self.unlink(cr, SUPERUSER_ID, [x.id for x in tail_opportunities], context=context) - - return highest.id - - def _convert_opportunity_data(self, cr, uid, lead, customer, section_id=False, context=None): - crm_stage = self.pool.get('crm.case.stage') - contact_id = False - if customer: - contact_id = self.pool.get('res.partner').address_get(cr, uid, [customer.id])['default'] - if not section_id: - section_id = lead.section_id and lead.section_id.id or False - val = { - 'planned_revenue': lead.planned_revenue, - 'probability': lead.probability, - 'name': lead.name, - 'partner_id': customer and customer.id or False, - 'user_id': (lead.user_id and lead.user_id.id), - 'type': 'opportunity', - 'date_action': fields.datetime.now(), - 'date_open': fields.datetime.now(), - 'email_from': customer and customer.email or lead.email_from, - 'phone': customer and customer.phone or lead.phone, - } - if not lead.stage_id or lead.stage_id.type=='lead': - val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, [('state', '=', 'draft'),('type', 'in', ('opportunity','both'))], context=context) - return val - - def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None): - customer = False - if partner_id: - partner = self.pool.get('res.partner') - customer = partner.browse(cr, uid, partner_id, context=context) - for lead in self.browse(cr, uid, ids, context=context): - if lead.state in ('done', 'cancel'): - continue - vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context) - self.write(cr, uid, [lead.id], vals, context=context) - self.message_post(cr, uid, ids, body=_("Lead converted into an Opportunity"), subtype="crm.mt_lead_convert_to_opportunity", context=context) - - if user_ids or section_id: - self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context) - - return True - - def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): - partner = self.pool.get('res.partner') - vals = {'name': name, - 'user_id': lead.user_id.id, - 'comment': lead.description, - 'section_id': lead.section_id.id or False, - 'parent_id': parent_id, - 'phone': lead.phone, - 'mobile': lead.mobile, - 'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, - 'fax': lead.fax, - 'title': lead.title and lead.title.id or False, - 'function': lead.function, - 'street': lead.street, - 'street2': lead.street2, - 'zip': lead.zip, - 'city': lead.city, - 'country_id': lead.country_id and lead.country_id.id or False, - 'state_id': lead.state_id and lead.state_id.id or False, - 'is_company': is_company, - 'type': 'contact' - } - partner = partner.create(cr, uid, vals, context=context) - return partner - - def _create_lead_partner(self, cr, uid, lead, context=None): - partner_id = False - if lead.partner_name and lead.contact_name: - partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) - partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) - elif lead.partner_name and not lead.contact_name: - partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) - elif not lead.partner_name and lead.contact_name: - partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, context=context) - elif lead.email_from and self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0]: - contact_name = self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0] - partner_id = self._lead_create_contact(cr, uid, lead, contact_name, False, context=context) - else: - raise osv.except_osv( - _('Warning!'), - _('No customer name defined. Please fill one of the following fields: Company Name, Contact Name or Email ("Name ")') - ) - return partner_id - - def _lead_set_partner(self, cr, uid, lead, partner_id, context=None): - """ - Assign a partner to a lead. - - :param object lead: browse record of the lead to process - :param int partner_id: identifier of the partner to assign - :return bool: True if the partner has properly been assigned - """ - res = False - res_partner = self.pool.get('res.partner') - if partner_id: - res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id and lead.section_id.id or False}) - contact_id = res_partner.address_get(cr, uid, [partner_id])['default'] - res = lead.write({'partner_id': partner_id}, context=context) - message = _("Partner set to %s." % (lead.partner_id.name)) - self.message_post(cr, uid, [lead.id], body=message, context=context) - return res - - def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None): - """ - Handle partner assignation during a lead conversion. - if action is 'create', create new partner with contact and assign lead to new partner_id. - otherwise assign lead to the specified partner_id - - :param list ids: leads/opportunities ids to process - :param string action: what has to be done regarding partners (create it, assign an existing one, or nothing) - :param int partner_id: partner to assign if any - :return dict: dictionary organized as followed: {lead_id: partner_assigned_id} - """ - #TODO this is a duplication of the handle_partner_assignation method of crm_phonecall - partner_ids = {} - # If a partner_id is given, force this partner for all elements - force_partner_id = partner_id - for lead in self.browse(cr, uid, ids, context=context): - # If the action is set to 'create' and no partner_id is set, create a new one - if action == 'create': - partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context) - self._lead_set_partner(cr, uid, lead, partner_id, context=context) - partner_ids[lead.id] = partner_id - return partner_ids - - def allocate_salesman(self, cr, uid, ids, user_ids=None, team_id=False, context=None): - """ - Assign salesmen and salesteam to a batch of leads. If there are more - leads than salesmen, these salesmen will be assigned in round-robin. - E.g.: 4 salesmen (S1, S2, S3, S4) for 6 leads (L1, L2, ... L6). They - will be assigned as followed: L1 - S1, L2 - S2, L3 - S3, L4 - S4, - L5 - S1, L6 - S2. - - :param list ids: leads/opportunities ids to process - :param list user_ids: salesmen to assign - :param int team_id: salesteam to assign - :return bool - """ - index = 0 - - for lead_id in ids: - value = {} - if team_id: - value['section_id'] = team_id - if user_ids: - value['user_id'] = user_ids[index] - # Cycle through user_ids - index = (index + 1) % len(user_ids) - if value: - self.write(cr, uid, [lead_id], value, context=context) - return True - - def schedule_phonecall(self, cr, uid, ids, schedule_time, call_summary, desc, phone, contact_name, user_id=False, section_id=False, categ_id=False, action='schedule', context=None): - """ - :param string action: ('schedule','Schedule a call'), ('log','Log a call') - """ - phonecall = self.pool.get('crm.phonecall') - model_data = self.pool.get('ir.model.data') - phonecall_dict = {} - if not categ_id: - try: - res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') - categ_id = model_data.browse(cr, uid, res_id, context=context).res_id - except ValueError: - pass - for lead in self.browse(cr, uid, ids, context=context): - if not section_id: - section_id = lead.section_id and lead.section_id.id or False - if not user_id: - user_id = lead.user_id and lead.user_id.id or False - vals = { - 'name': call_summary, - 'opportunity_id': lead.id, - 'user_id': user_id or False, - 'categ_id': categ_id or False, - 'description': desc or '', - 'date': schedule_time, - 'section_id': section_id or False, - 'partner_id': lead.partner_id and lead.partner_id.id or False, - 'partner_phone': phone or lead.phone or (lead.partner_id and lead.partner_id.phone or False), - 'partner_mobile': lead.partner_id and lead.partner_id.mobile or False, - 'priority': lead.priority, - } - new_id = phonecall.create(cr, uid, vals, context=context) - phonecall.case_open(cr, uid, [new_id], context=context) - if action == 'log': - phonecall.case_close(cr, uid, [new_id], context=context) - phonecall_dict[lead.id] = new_id - self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context) - return phonecall_dict - - def redirect_opportunity_view(self, cr, uid, opportunity_id, context=None): - models_data = self.pool.get('ir.model.data') - - # Get opportunity views - dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor') - dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_oppor') - return { - 'name': _('Opportunity'), - 'view_type': 'form', - 'view_mode': 'tree, form', - 'res_model': 'crm.lead', - 'domain': [('type', '=', 'opportunity')], - 'res_id': int(opportunity_id), - 'view_id': False, - 'views': [(form_view or False, 'form'), - (tree_view or False, 'tree'), - (False, 'calendar'), (False, 'graph')], - 'type': 'ir.actions.act_window', - } - - def redirect_lead_view(self, cr, uid, lead_id, context=None): - models_data = self.pool.get('ir.model.data') - - # Get lead views - dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_leads') - dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_leads') - return { - 'name': _('Lead'), - 'view_type': 'form', - 'view_mode': 'tree, form', - 'res_model': 'crm.lead', - 'domain': [('type', '=', 'lead')], - 'res_id': int(lead_id), - 'view_id': False, - 'views': [(form_view or False, 'form'), - (tree_view or False, 'tree'), - (False, 'calendar'), (False, 'graph')], - 'type': 'ir.actions.act_window', - } - - def action_makeMeeting(self, cr, uid, ids, context=None): - """ - Open meeting's calendar view to schedule meeting on current opportunity. - :return dict: dictionary value for created Meeting view - """ - opportunity = self.browse(cr, uid, ids[0], context) - res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'base_calendar', 'action_crm_meeting', context) - res['context'] = { - 'search_default_opportunity_id': opportunity.id, - 'default_opportunity_id': opportunity.id, - 'default_partner_id': opportunity.partner_id and opportunity.partner_id.id or False, - 'default_partner_ids' : opportunity.partner_id and [opportunity.partner_id.id] or False, - 'default_user_id': uid, - 'default_section_id': opportunity.section_id and opportunity.section_id.id or False, - 'default_email_from': opportunity.email_from, - 'default_name': opportunity.name, - } - return res - - def write(self, cr, uid, ids, vals, context=None): - if vals.get('stage_id'): - stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context) - if not vals.get('probability') and stage.on_change: - # change probability of lead(s) if required by stage - vals['probability'] = stage.probability - # set closed date when won or lost - if not vals.get('date_closed') and (vals.get('probability', 0) >= 100 or stage.state == 'canceled'): - vals['date_closed'] = fields.datetime.now() - return super(crm_lead, self).write(cr, uid, ids, vals, context=context) - - def copy(self, cr, uid, id, default=None, context=None): - if not default: - default = {} - if not context: - context = {} - lead = self.browse(cr, uid, id, context=context) - local_context = dict(context) - local_context.setdefault('default_type', lead.type) - local_context.setdefault('default_section_id', lead.section_id) - if lead.type == 'opportunity': - default['date_open'] = fields.datetime.now() - else: - default['date_open'] = False - default['date_closed'] = False - default['stage_id'] = self._get_default_stage_id(cr, uid, local_context) - return super(crm_lead, self).copy(cr, uid, id, default, context=context) - - def new_mail_send(self, cr, uid, ids, context=None): - ''' - This function opens a window to compose an email, with the edi sale template message loaded by default - ''' - assert len(ids) == 1, 'This option should only be used for a single id at a time.' - ir_model_data = self.pool.get('ir.model.data') - try: - template_id = ir_model_data.get_object_reference(cr, uid, 'crm', 'email_template_opportunity_mail')[1] - except ValueError: - template_id = False - try: - compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] - except ValueError: - compose_form_id = False - if context is None: - context = {} - ctx = context.copy() - ctx.update({ - 'default_model': 'crm.lead', - 'default_res_id': ids[0], - 'default_use_template': bool(template_id), - 'default_template_id': template_id, - 'default_composition_mode': 'comment', - }) - return { - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'mail.compose.message', - 'views': [(compose_form_id, 'form')], - 'view_id': compose_form_id, - 'target': 'new', - 'context': ctx, - } - - # ---------------------------------------- - # Mail Gateway - # ---------------------------------------- - - def message_get_reply_to(self, cr, uid, ids, context=None): - """ Override to get the reply_to of the parent project. """ - return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False - for lead in self.browse(cr, SUPERUSER_ID, ids, context=context)] - - def message_get_suggested_recipients(self, cr, uid, ids, context=None): - recipients = super(crm_lead, self).message_get_suggested_recipients(cr, uid, ids, context=context) - try: - for lead in self.browse(cr, uid, ids, context=context): - if lead.partner_id: - self._message_add_suggested_recipient(cr, uid, recipients, lead, partner=lead.partner_id, reason=_('Customer')) - elif lead.email_from: - self._message_add_suggested_recipient(cr, uid, recipients, lead, email=lead.email_from, reason=_('Customer Email')) - except (osv.except_osv, orm.except_orm): # no read access rights -> just ignore suggested recipients because this imply modifying followers - pass - return recipients - - def message_new(self, cr, uid, msg, custom_values=None, context=None): - """ Overrides mail_thread message_new that is called by the mailgateway - through message_process. - This override updates the document according to the email. - """ - if custom_values is None: - custom_values = {} - desc = html2plaintext(msg.get('body')) if msg.get('body') else '' - defaults = { - 'name': msg.get('subject') or _("No Subject"), - 'description': desc, - 'email_from': msg.get('from'), - 'email_cc': msg.get('cc'), - 'partner_id': msg.get('author_id', False), - 'user_id': False, - } - if msg.get('author_id'): - defaults.update(self.on_change_partner(cr, uid, None, msg.get('author_id'), context=context)['value']) - if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - defaults['priority'] = msg.get('priority') - defaults.update(custom_values) - return super(crm_lead, self).message_new(cr, uid, msg, custom_values=defaults, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: update_vals = {} - - if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - update_vals['priority'] = msg.get('priority') - maps = { - 'cost':'planned_cost', - 'revenue': 'planned_revenue', - 'probability':'probability', - } - for line in msg.get('body', '').split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower()): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) - - # ---------------------------------------- - # OpenChatter methods and notifications - # ---------------------------------------- - - def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): - phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] - if action == 'log': - message = _('Logged a call for %(date)s. %(description)s') - else: - message = _('Scheduled a call for %(date)s. %(description)s') - phonecall_date = datetime.strptime(phonecall.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) - phonecall_usertime = fields.datetime.context_timestamp(cr, uid, phonecall_date, context=context).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) - html_time = "" % (phonecall.date, phonecall_usertime) - message = message % dict(date=html_time, description=phonecall.description) - return self.message_post(cr, uid, ids, body=message, context=context) - - def log_meeting(self, cr, uid, ids, meeting_subject, meeting_date, duration, context=None): - if not duration: - duration = _('unknown') - else: - duration = str(duration) - message = _("Meeting scheduled at '%s'
    Subject: %s
    Duration: %s hour(s)") % (meeting_date, meeting_subject, duration) - return self.message_post(cr, uid, ids, body=message, context=context) - - def onchange_state(self, cr, uid, ids, state_id, context=None): - if state_id: - country_id=self.pool.get('res.country.state').browse(cr, uid, state_id, context).country_id.id - return {'value':{'country_id':country_id}} - return {} - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.addons.base_status.base_stage import base_stage +import crm +from datetime import datetime +from operator import itemgetter +from openerp.osv import fields, osv, orm +import time +from openerp import SUPERUSER_ID +from openerp import tools +from openerp.tools.translate import _ +from openerp.tools import html2plaintext +from base.res.res_partner import format_address +CRM_LEAD_FIELDS_TO_MERGE = ['name', + 'partner_id', + 'channel_id', + 'company_id', + 'country_id', + 'section_id', + 'state_id', + 'stage_id', + 'type_id', + 'user_id', + 'title', + 'city', + 'contact_name', + 'description', + 'email', + 'fax', + 'mobile', + 'partner_name', + 'phone', + 'probability', + 'planned_revenue', + 'street', + 'street2', + 'zip', + 'create_date', + 'date_action_last', + 'date_action_next', + 'email_from', + 'email_cc', + 'partner_name'] +CRM_LEAD_PENDING_STATES = (crm.AVAILABLE_STATES[2][0], crm.AVAILABLE_STATES[3][0], crm.AVAILABLE_STATES[4][0]) + +class crm_lead(base_stage, format_address, osv.osv): + """ CRM Lead Case """ + _name = 'crm.lead' + _description = 'Lead/Opportunity' + _order = 'priority,date_action,id desc' + _inherit = ['mail.thread', 'ir.needaction_mixin'] + _track = {'state': {'crm.mt_lead_create': lambda self, cr, uid, obj, ctx = None: obj['state'] in ('new', 'draft'), + 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx = None: obj['state'] == 'done', + 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx = None: obj['state'] == 'cancel'}, + 'stage_id': {'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx = None: obj['state'] not in ('new', 'draft', 'cancel', 'done')}} + + def create(self, cr, uid, vals, context = None): + if context is None: + context = {} + if vals.get('type') and not context.get('default_type'): + context['default_type'] = vals.get('type') + if vals.get('section_id') and not context.get('default_section_id'): + context['default_section_id'] = vals.get('section_id') + create_context = dict(context, mail_create_nolog=True) + return super(crm_lead, self).create(cr, uid, vals, context=create_context) + + def _get_default_section_id(self, cr, uid, context = None): + """ Gives default section by checking if present in the context """ + return self._resolve_section_id_from_context(cr, uid, context=context) or False + + def _get_default_stage_id(self, cr, uid, context = None): + """ Gives default stage_id """ + section_id = self._get_default_section_id(cr, uid, context=context) + return self.stage_find(cr, uid, [], section_id, [('state', '=', 'draft')], context=context) + + def _resolve_section_id_from_context(self, cr, uid, context = None): + """ Returns ID of section based on the value of 'section_id' + context key, or None if it cannot be resolved to a single + Sales Team. + """ + if context is None: + context = {} + if type(context.get('default_section_id')) in (int, long): + return context.get('default_section_id') + else: + if isinstance(context.get('default_section_id'), basestring): + section_name = context['default_section_id'] + section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=section_name, context=context) + if len(section_ids) == 1: + return int(section_ids[0][0]) + return + + def _resolve_type_from_context(self, cr, uid, context = None): + """ Returns the type (lead or opportunity) from the type context + key. Returns None if it cannot be resolved. + """ + if context is None: + context = {} + return context.get('default_type') + + def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order = None, access_rights_uid = None, context = None): + access_rights_uid = access_rights_uid or uid + stage_obj = self.pool.get('crm.case.stage') + order = stage_obj._order + if read_group_order == 'stage_id desc': + order = '%s desc' % order + search_domain = [] + section_id = self._resolve_section_id_from_context(cr, uid, context=context) + if section_id: + search_domain += ['|', ('section_ids', '=', section_id)] + search_domain += [('id', 'in', ids)] + else: + search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)] + type = self._resolve_type_from_context(cr, uid, context=context) + if type: + search_domain += ['|', ('type', '=', type), ('type', '=', 'both')] + stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) + result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) + result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) + fold = {} + for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): + fold[stage.id] = stage.fold or False + + return (result, fold) + + def fields_view_get(self, cr, user, view_id = None, view_type = 'form', context = None, toolbar = False, submenu = False): + res = super(crm_lead, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu) + if view_type == 'form': + res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context) + return res + + _group_by_full = {'stage_id': _read_group_stage_ids} + + def _compute_day(self, cr, uid, ids, fields, args, context = None): + """ + :return dict: difference between current date and log date + """ + cal_obj = self.pool.get('resource.calendar') + res_obj = self.pool.get('resource.resource') + res = {} + for lead in self.browse(cr, uid, ids, context=context): + for field in fields: + res[lead.id] = {} + duration = 0 + ans = False + if field == 'day_open': + if lead.date_open: + date_create = datetime.strptime(lead.create_date, '%Y-%m-%d %H:%M:%S') + date_open = datetime.strptime(lead.date_open, '%Y-%m-%d %H:%M:%S') + ans = date_open - date_create + date_until = lead.date_open + elif field == 'day_close': + if lead.date_closed: + date_create = datetime.strptime(lead.create_date, '%Y-%m-%d %H:%M:%S') + date_close = datetime.strptime(lead.date_closed, '%Y-%m-%d %H:%M:%S') + date_until = lead.date_closed + ans = date_close - date_create + if ans: + resource_id = False + if lead.user_id: + resource_ids = res_obj.search(cr, uid, [('user_id', '=', lead.user_id.id)]) + if len(resource_ids): + resource_id = resource_ids[0] + duration = float(ans.days) + if lead.section_id and lead.section_id.resource_calendar_id: + duration = float(ans.days) * 24 + new_dates = cal_obj.interval_get(cr, uid, lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False, datetime.strptime(lead.create_date, '%Y-%m-%d %H:%M:%S'), duration, resource=resource_id) + no_days = [] + date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S') + for in_time, out_time in new_dates: + if in_time.date not in no_days: + no_days.append(in_time.date) + if out_time > date_until: + break + + duration = len(no_days) + res[lead.id][field] = abs(int(duration)) + + return res + + def _history_search(self, cr, uid, obj, name, args, context = None): + res = [] + msg_obj = self.pool.get('mail.message') + message_ids = msg_obj.search(cr, uid, [('email_from', '!=', False), ('subject', args[0][1], args[0][2])], context=context) + lead_ids = self.search(cr, uid, [('message_ids', 'in', message_ids)], context=context) + if lead_ids: + return [('id', 'in', lead_ids)] + else: + return [('id', '=', '0')] + + _columns = {'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', track_visibility='onchange', select=True, help='Linked partner (optional). Usually created when converting the lead.'), + 'id': fields.integer('ID', readonly=True), + 'name': fields.char('Subject', size=64, required=True, select=1), + 'active': fields.boolean('Active', required=False), + 'date_action_last': fields.datetime('Last Action', readonly=1), + 'date_action_next': fields.datetime('Next Action', readonly=1), + 'email_from': fields.char('Email', size=128, help='Email address of the contact', select=1), + 'section_id': fields.many2one('crm.case.section', 'Sales Team', select=True, track_visibility='onchange', help='When sending mails, the default email address is taken from the sales team.'), + 'create_date': fields.datetime('Creation Date', readonly=True), + 'email_cc': fields.text('Global CC', size=252, help='These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma'), + 'description': fields.text('Notes'), + 'write_date': fields.datetime('Update Date', readonly=True), + 'categ_ids': fields.many2many('crm.case.categ', 'crm_lead_category_rel', 'lead_id', 'category_id', 'Categories', domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]"), + 'type_id': fields.many2one('crm.case.resource.type', 'Campaign', domain="['|',('section_id','=',section_id),('section_id','=',False)]", help='From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?'), + 'channel_id': fields.many2one('crm.case.channel', 'Channel', help='Communication channel (mail, direct, phone, ...)'), + 'contact_name': fields.char('Contact Name', size=64), + 'partner_name': fields.char('Customer Name', size=64, help='The name of the future partner company that will be created while converting the lead into opportunity', select=1), + 'opt_out': fields.boolean('Opt-Out', oldname='optout', help="If opt-out is checked, this contact has refused to receive emails for mass mailing and marketing campaign. Filter 'Available for Mass Mailing' allows users to filter the leads when performing mass mailing."), + 'type': fields.selection([('lead', 'Lead'), ('opportunity', 'Opportunity')], 'Type', help='Type is used to separate Leads and Opportunities'), + 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), + 'date_closed': fields.datetime('Closed', readonly=True), + 'stage_id': fields.many2one('crm.case.stage', 'Stage', track_visibility='onchange', domain="['&', '&', ('fold', '=', False), ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"), + 'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'), + 'referred': fields.char('Referred By', size=64), + 'date_open': fields.datetime('Opened', readonly=True), + 'day_open': fields.function(_compute_day, string='Days to Open', multi='day_open', type='float', store=True), + 'day_close': fields.function(_compute_day, string='Days to Close', multi='day_close', type='float', store=True), + 'state': fields.related('stage_id', 'state', type='selection', store=True, selection=crm.AVAILABLE_STATES, string='Status', readonly=True, help="The Status is set to 'Draft', when a case is created. If the case is in progress the Status is set to 'Open'. When the case is over, the Status is set to 'Done'. If the case needs to be reviewed then the Status is set to 'Pending'."), + 'probability': fields.float('Success Rate (%)', group_operator='avg'), + 'planned_revenue': fields.float('Expected Revenue', track_visibility='always'), + 'ref': fields.reference('Reference', selection=crm._links_get, size=128), + 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), + 'phone': fields.char('Phone', size=64), + 'date_deadline': fields.date('Expected Closing', help='Estimate of the date on which the opportunity will be won.'), + 'date_action': fields.date('Next Action Date', select=True), + 'title_action': fields.char('Next Action', size=64), + 'color': fields.integer('Color Index'), + 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), + 'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True), + 'company_currency': fields.related('company_id', 'currency_id', type='many2one', string='Currency', readonly=True, relation='res.currency'), + 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), + 'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True), + 'street': fields.char('Street', size=128), + 'street2': fields.char('Street2', size=128), + 'zip': fields.char('Zip', change_default=True, size=24), + 'city': fields.char('City', size=128), + 'state_id': fields.many2one('res.country.state', 'State'), + 'country_id': fields.many2one('res.country', 'Country'), + 'phone': fields.char('Phone', size=64), + 'fax': fields.char('Fax', size=64), + 'mobile': fields.char('Mobile', size=64), + 'function': fields.char('Function', size=128), + 'title': fields.many2one('res.partner.title', 'Title'), + 'company_id': fields.many2one('res.company', 'Company', select=1), + 'payment_mode': fields.many2one('crm.payment.mode', 'Payment Mode', domain="[('section_id','=',section_id)]"), + 'planned_cost': fields.float('Planned Costs')} + _defaults = {'active': 1, + 'type': 'lead', + 'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c), + 'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c), + 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), + 'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c), + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c), + 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], + 'color': 0} + _sql_constraints = [('check_probability', 'check(probability >= 0 and probability <= 100)', 'The probability of closing the deal should be between 0% and 100%!')] + + def onchange_stage_id(self, cr, uid, ids, stage_id, context = None): + if not stage_id: + return {'value': {}} + stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context) + if not stage.on_change: + return {'value': {}} + return {'value': {'probability': stage.probability}} + + def on_change_partner(self, cr, uid, ids, partner_id, context = None): + result = {} + values = {} + if partner_id: + partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) + values = {'partner_name': partner.name, + 'street': partner.street, + 'street2': partner.street2, + 'city': partner.city, + 'state_id': partner.state_id and partner.state_id.id or False, + 'country_id': partner.country_id and partner.country_id.id or False, + 'email_from': partner.email, + 'phone': partner.phone, + 'mobile': partner.mobile, + 'fax': partner.fax} + return {'value': values} + + def on_change_user(self, cr, uid, ids, user_id, context = None): + """ When changing the user, also set a section_id or restrict section id + to the ones user_id is member of. """ + if user_id: + section_ids = self.pool.get('crm.case.section').search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)], context=context) + if section_ids: + return {'value': {'section_id': section_ids[0]}} + return {'value': {}} + + def _check(self, cr, uid, ids = False, context = None): + """ Override of the base.stage method. + Function called by the scheduler to process cases for date actions + Only works on not done and cancelled cases + """ + cr.execute("select * from crm_case where (date_action_last<%s or date_action_last is null) and (date_action_next<=%s or date_action_next is null) and state not in ('cancel','done')", (time.strftime('%Y-%m-%d %H:%M:%S'), time.strftime('%Y-%m-%d %H:%M:%S'))) + ids2 = map(lambda x: x[0], cr.fetchall() or []) + cases = self.browse(cr, uid, ids2, context=context) + return self._action(cr, uid, cases, False, context=context) + + def stage_find(self, cr, uid, cases, section_id, domain = None, order = 'sequence', context = None): + """ Override of the base.stage method + Parameter of the stage search taken from the lead: + - type: stage type must be the same or 'both' + - section_id: if set, stages must belong to this section or + be a default stage; if not set, stages must be default + stages + """ + if isinstance(cases, (int, long)): + cases = self.browse(cr, uid, cases, context=context) + section_ids = [] + types = ['both'] + if not cases: + type = context.get('default_type') + types += [type] + if section_id: + section_ids.append(section_id) + for lead in cases: + if lead.section_id: + section_ids.append(lead.section_id.id) + if lead.type not in types: + types.append(lead.type) + + search_domain = [] + if section_ids: + search_domain += ['|'] * len(section_ids) + for section_id in section_ids: + search_domain.append(('section_ids', '=', section_id)) + + else: + search_domain.append(('case_default', '=', True)) + search_domain.append(('type', 'in', types)) + search_domain += list(domain) + stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, context=context) + if stage_ids: + return stage_ids[0] + return False + + def case_cancel(self, cr, uid, ids, context = None): + """ Overrides case_cancel from base_stage to set probability """ + res = super(crm_lead, self).case_cancel(cr, uid, ids, context=context) + self.write(cr, uid, ids, {'probability': 0.0}, context=context) + return res + + def case_reset(self, cr, uid, ids, context = None): + """ Overrides case_reset from base_stage to set probability """ + res = super(crm_lead, self).case_reset(cr, uid, ids, context=context) + self.write(cr, uid, ids, {'probability': 0.0}, context=context) + return res + + def case_mark_lost(self, cr, uid, ids, context = None): + """ Mark the case as lost: state=cancel and probability=0 """ + for lead in self.browse(cr, uid, ids): + stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0), ('on_change', '=', True)], context=context) + if stage_id: + self.case_set(cr, uid, [lead.id], values_to_update={'probability': 0.0}, new_stage_id=stage_id, context=context) + + return True + + def case_mark_won(self, cr, uid, ids, context = None): + """ Mark the case as won: state=done and probability=100 """ + for lead in self.browse(cr, uid, ids): + stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0), ('on_change', '=', True)], context=context) + if stage_id: + self.case_set(cr, uid, [lead.id], values_to_update={'probability': 100.0}, new_stage_id=stage_id, context=context) + + return True + + def set_priority(self, cr, uid, ids, priority): + """ Set lead priority + """ + return self.write(cr, uid, ids, {'priority': priority}) + + def set_high_priority(self, cr, uid, ids, context = None): + """ Set lead priority to high + """ + return self.set_priority(cr, uid, ids, '1') + + def set_normal_priority(self, cr, uid, ids, context = None): + """ Set lead priority to normal + """ + return self.set_priority(cr, uid, ids, '3') + + def _merge_get_result_type(self, cr, uid, opps, context = None): + """ + Define the type of the result of the merge. If at least one of the + element to merge is an opp, the resulting new element will be an opp. + Otherwise it will be a lead. + + We'll directly use a list of browse records instead of a list of ids + for performances' sake: it will spare a second browse of the + leads/opps. + + :param list opps: list of browse records containing the leads/opps to process + :return string type: the type of the final element + """ + for opp in opps: + if opp.type == 'opportunity': + return 'opportunity' + + return 'lead' + + def _merge_data(self, cr, uid, ids, oldest, fields, context = None): + """ + Prepare lead/opp data into a dictionary for merging. Different types + of fields are processed in different ways: + - text: all the values are concatenated + - m2m and o2m: those fields aren't processed + - m2o: the first not null value prevails (the other are dropped) + - any other type of field: same as m2o + + :param list ids: list of ids of the leads to process + :param list fields: list of leads' fields to process + :return dict data: contains the merged values + """ + opportunities = self.browse(cr, uid, ids, context=context) + + def _get_first_not_null(attr): + for opp in opportunities: + if hasattr(opp, attr) and bool(getattr(opp, attr)): + return getattr(opp, attr) + + return False + + def _get_first_not_null_id(attr): + res = _get_first_not_null(attr) + return res and res.id or False + + def _concat_all(attr): + return '\n\n'.join(filter(lambda x: x, [ getattr(opp, attr) or '' for opp in opportunities if hasattr(opp, attr) ])) + + data = {} + for field_name in fields: + field_info = self._all_columns.get(field_name) + if field_info is None: + continue + field = field_info.column + if field._type in ('many2many', 'one2many'): + continue + elif field._type == 'many2one': + data[field_name] = _get_first_not_null_id(field_name) + elif field._type == 'text': + data[field_name] = _concat_all(field_name) + else: + data[field_name] = _get_first_not_null(field_name) + + data['type'] = self._merge_get_result_type(cr, uid, opportunities, context) + return data + + def _mail_body(self, cr, uid, lead, fields, title = False, context = None): + body = [] + if title: + body.append('%s\n' % title) + for field_name in fields: + field_info = self._all_columns.get(field_name) + if field_info is None: + continue + field = field_info.column + value = '' + if field._type == 'selection': + if hasattr(field.selection, '__call__'): + key = field.selection(self, cr, uid, context=context) + else: + key = field.selection + value = dict(key).get(lead[field_name], lead[field_name]) + elif field._type == 'many2one': + if lead[field_name]: + value = lead[field_name].name_get()[0][1] + elif field._type == 'many2many': + if lead[field_name]: + for val in lead[field_name]: + field_value = val.name_get()[0][1] + value += field_value + ',' + + else: + value = lead[field_name] + body.append('%s: %s' % (field.string, value or '')) + + return '
    '.join(body + ['
    ']) + + def _merge_notify(self, cr, uid, opportunity_id, opportunities, context = None): + """ + Create a message gathering merged leads/opps information. + """ + details = [] + result_type = self._merge_get_result_type(cr, uid, opportunities, context) + if result_type == 'lead': + merge_message = _('Merged leads') + else: + merge_message = _('Merged opportunities') + subject = [merge_message] + for opportunity in opportunities: + subject.append(opportunity.name) + title = '%s : %s' % (opportunity.type == 'opportunity' and _('Merged opportunity') or _('Merged lead'), opportunity.name) + fields = list(CRM_LEAD_FIELDS_TO_MERGE) + details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context)) + + subject = subject[0] + ': ' + ', '.join(subject[1:]) + details = '\n\n'.join(details) + return self.message_post(cr, uid, [opportunity_id], body=details, subject=subject, context=context) + + def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context = None): + message = self.pool.get('mail.message') + for opportunity in opportunities: + for history in opportunity.message_ids: + message.write(cr, uid, history.id, {'res_id': opportunity_id, + 'subject': _('From %s : %s') % (opportunity.name, history.subject)}, context=context) + + return True + + def _merge_opportunity_attachments(self, cr, uid, opportunity_id, opportunities, context = None): + attach_obj = self.pool.get('ir.attachment') + + def _get_attachments(opportunity_id): + attachment_ids = attach_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context) + return attach_obj.browse(cr, uid, attachment_ids, context=context) + + first_attachments = _get_attachments(opportunity_id) + count = 1 + for opportunity in opportunities: + attachments = _get_attachments(opportunity.id) + for attachment in attachments: + values = {'res_id': opportunity_id} + for attachment_in_first in first_attachments: + if attachment.name == attachment_in_first.name: + name = ('%s (%s)' % (attachment.name, count),) + + count += 1 + attachment.write(values) + + return True + + def merge_opportunity(self, cr, uid, ids, context = None): + """ + Different cases of merge: + - merge leads together = 1 new lead + - merge at least 1 opp with anything else (lead or opp) = 1 new opp + + :param list ids: leads/opportunities ids to merge + :return int id: id of the resulting lead/opp + """ + if context is None: + context = {} + if len(ids) <= 1: + raise osv.except_osv(_('Warning!'), _('Please select more than one element (lead or opportunity) from the list view.')) + opportunities = self.browse(cr, uid, ids, context=context) + sequenced_opps = [] + for opportunity in opportunities: + sequence = -1 + if opportunity.stage_id and opportunity.stage_id.state != 'cancel': + sequence = opportunity.stage_id.sequence + sequenced_opps.append(((int(sequence != -1 and opportunity.type == 'opportunity'), sequence, -opportunity.id), opportunity)) + + sequenced_opps.sort(reverse=True) + opportunities = map(itemgetter(1), sequenced_opps) + ids = [ opportunity.id for opportunity in opportunities ] + highest = opportunities[0] + opportunities_rest = opportunities[1:] + tail_opportunities = opportunities_rest + fields = list(CRM_LEAD_FIELDS_TO_MERGE) + merged_data = self._merge_data(cr, uid, ids, highest, fields, context=context) + self._merge_opportunity_history(cr, uid, highest.id, tail_opportunities, context=context) + self._merge_opportunity_attachments(cr, uid, highest.id, tail_opportunities, context=context) + opportunities = [highest] + opportunities.extend(opportunities_rest) + self._merge_notify(cr, uid, highest.id, opportunities, context=context) + if merged_data.get('section_id'): + section_stage_ids = self.pool.get('crm.case.stage').search(cr, uid, [('section_ids', 'in', merged_data['section_id']), ('type', '=', merged_data.get('type'))], order='sequence', context=context) + if merged_data.get('stage_id') not in section_stage_ids: + merged_data['stage_id'] = section_stage_ids and section_stage_ids[0] or False + self.write(cr, uid, [highest.id], merged_data, context=context) + self.unlink(cr, SUPERUSER_ID, [ x.id for x in tail_opportunities ], context=context) + return highest.id + + def _convert_opportunity_data(self, cr, uid, lead, customer, section_id = False, context = None): + crm_stage = self.pool.get('crm.case.stage') + contact_id = False + if customer: + contact_id = self.pool.get('res.partner').address_get(cr, uid, [customer.id])['default'] + if not section_id: + section_id = lead.section_id and lead.section_id.id or False + val = {'planned_revenue': lead.planned_revenue, + 'probability': lead.probability, + 'name': lead.name, + 'partner_id': customer and customer.id or False, + 'user_id': lead.user_id and lead.user_id.id, + 'type': 'opportunity', + 'date_action': fields.datetime.now(), + 'date_open': fields.datetime.now(), + 'email_from': customer and customer.email or lead.email_from, + 'phone': customer and customer.phone or lead.phone} + if not lead.stage_id or lead.stage_id.type == 'lead': + val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, [('state', '=', 'draft'), ('type', 'in', ('opportunity', 'both'))], context=context) + return val + + def convert_opportunity(self, cr, uid, ids, partner_id, user_ids = False, section_id = False, context = None): + customer = False + if partner_id: + partner = self.pool.get('res.partner') + customer = partner.browse(cr, uid, partner_id, context=context) + for lead in self.browse(cr, uid, ids, context=context): + if lead.state in ('done', 'cancel'): + continue + vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context) + self.write(cr, uid, [lead.id], vals, context=context) + + self.message_post(cr, uid, ids, body=_('Lead converted into an Opportunity'), subtype='crm.mt_lead_convert_to_opportunity', context=context) + if user_ids or section_id: + self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context) + return True + + def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id = False, context = None): + partner = self.pool.get('res.partner') + vals = {'name': name, + 'user_id': lead.user_id.id, + 'comment': lead.description, + 'section_id': lead.section_id.id or False, + 'parent_id': parent_id, + 'phone': lead.phone, + 'mobile': lead.mobile, + 'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, + 'fax': lead.fax, + 'title': lead.title and lead.title.id or False, + 'function': lead.function, + 'street': lead.street, + 'street2': lead.street2, + 'zip': lead.zip, + 'city': lead.city, + 'country_id': lead.country_id and lead.country_id.id or False, + 'state_id': lead.state_id and lead.state_id.id or False, + 'is_company': is_company, + 'type': 'contact'} + partner = partner.create(cr, uid, vals, context=context) + return partner + + def _create_lead_partner(self, cr, uid, lead, context = None): + partner_id = False + if lead.partner_name and lead.contact_name: + partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) + partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) + elif lead.partner_name and not lead.contact_name: + partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) + elif not lead.partner_name and lead.contact_name: + partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, context=context) + elif lead.email_from and self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0]: + contact_name = self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0] + partner_id = self._lead_create_contact(cr, uid, lead, contact_name, False, context=context) + else: + raise osv.except_osv(_('Warning!'), _('No customer name defined. Please fill one of the following fields: Company Name, Contact Name or Email ("Name ")')) + return partner_id + + def _lead_set_partner(self, cr, uid, lead, partner_id, context = None): + """ + Assign a partner to a lead. + + :param object lead: browse record of the lead to process + :param int partner_id: identifier of the partner to assign + :return bool: True if the partner has properly been assigned + """ + res = False + res_partner = self.pool.get('res.partner') + if partner_id: + res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id and lead.section_id.id or False}) + contact_id = res_partner.address_get(cr, uid, [partner_id])['default'] + res = lead.write({'partner_id': partner_id}, context=context) + message = _('Partner set to %s.' % lead.partner_id.name) + self.message_post(cr, uid, [lead.id], body=message, context=context) + return res + + def handle_partner_assignation(self, cr, uid, ids, action = 'create', partner_id = False, context = None): + """ + Handle partner assignation during a lead conversion. + if action is 'create', create new partner with contact and assign lead to new partner_id. + otherwise assign lead to the specified partner_id + + :param list ids: leads/opportunities ids to process + :param string action: what has to be done regarding partners (create it, assign an existing one, or nothing) + :param int partner_id: partner to assign if any + :return dict: dictionary organized as followed: {lead_id: partner_assigned_id} + """ + partner_ids = {} + force_partner_id = partner_id + for lead in self.browse(cr, uid, ids, context=context): + if action == 'create': + partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context) + self._lead_set_partner(cr, uid, lead, partner_id, context=context) + partner_ids[lead.id] = partner_id + + return partner_ids + + def allocate_salesman(self, cr, uid, ids, user_ids = None, team_id = False, context = None): + """ + Assign salesmen and salesteam to a batch of leads. If there are more + leads than salesmen, these salesmen will be assigned in round-robin. + E.g.: 4 salesmen (S1, S2, S3, S4) for 6 leads (L1, L2, ... L6). They + will be assigned as followed: L1 - S1, L2 - S2, L3 - S3, L4 - S4, + L5 - S1, L6 - S2. + + :param list ids: leads/opportunities ids to process + :param list user_ids: salesmen to assign + :param int team_id: salesteam to assign + :return bool + """ + index = 0 + for lead_id in ids: + value = {} + if team_id: + value['section_id'] = team_id + if user_ids: + value['user_id'] = user_ids[index] + index = (index + 1) % len(user_ids) + if value: + self.write(cr, uid, [lead_id], value, context=context) + + return True + + def schedule_phonecall(self, cr, uid, ids, schedule_time, call_summary, desc, phone, contact_name, user_id = False, section_id = False, categ_id = False, action = 'schedule', context = None): + """ + :param string action: ('schedule','Schedule a call'), ('log','Log a call') + """ + phonecall = self.pool.get('crm.phonecall') + model_data = self.pool.get('ir.model.data') + phonecall_dict = {} + if not categ_id: + res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') + if res_id: + categ_id = model_data.browse(cr, uid, res_id, context=context).res_id + for lead in self.browse(cr, uid, ids, context=context): + if not section_id: + section_id = lead.section_id and lead.section_id.id or False + if not user_id: + user_id = lead.user_id and lead.user_id.id or False + vals = {'name': call_summary, + 'opportunity_id': lead.id, + 'user_id': user_id or False, + 'categ_id': categ_id or False, + 'description': desc or '', + 'date': schedule_time, + 'section_id': section_id or False, + 'partner_id': lead.partner_id and lead.partner_id.id or False, + 'partner_phone': phone or lead.phone or lead.partner_id and lead.partner_id.phone or False, + 'partner_mobile': lead.partner_id and lead.partner_id.mobile or False, + 'priority': lead.priority} + new_id = phonecall.create(cr, uid, vals, context=context) + phonecall.case_open(cr, uid, [new_id], context=context) + if action == 'log': + phonecall.case_close(cr, uid, [new_id], context=context) + phonecall_dict[lead.id] = new_id + self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context) + + return phonecall_dict + + def redirect_opportunity_view(self, cr, uid, opportunity_id, context = None): + models_data = self.pool.get('ir.model.data') + dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor') + dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_oppor') + return {'name': _('Opportunity'), + 'view_type': 'form', + 'view_mode': 'tree, form', + 'res_model': 'crm.lead', + 'domain': [('type', '=', 'opportunity')], + 'res_id': int(opportunity_id), + 'view_id': False, + 'views': [(form_view or False, 'form'), + (tree_view or False, 'tree'), + (False, 'calendar'), + (False, 'graph')], + 'type': 'ir.actions.act_window'} + + def redirect_lead_view(self, cr, uid, lead_id, context = None): + models_data = self.pool.get('ir.model.data') + dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_leads') + dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_leads') + return {'name': _('Lead'), + 'view_type': 'form', + 'view_mode': 'tree, form', + 'res_model': 'crm.lead', + 'domain': [('type', '=', 'lead')], + 'res_id': int(lead_id), + 'view_id': False, + 'views': [(form_view or False, 'form'), + (tree_view or False, 'tree'), + (False, 'calendar'), + (False, 'graph')], + 'type': 'ir.actions.act_window'} + + def action_makeMeeting(self, cr, uid, ids, context = None): + """ + Open meeting's calendar view to schedule meeting on current opportunity. + :return dict: dictionary value for created Meeting view + """ + opportunity = self.browse(cr, uid, ids[0], context) + res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'base_calendar', 'action_crm_meeting', context) + res['context'] = {'default_opportunity_id': opportunity.id, + 'default_partner_id': opportunity.partner_id and opportunity.partner_id.id or False, + 'default_partner_ids': opportunity.partner_id and [opportunity.partner_id.id] or False, + 'default_user_id': uid, + 'default_section_id': opportunity.section_id and opportunity.section_id.id or False, + 'default_email_from': opportunity.email_from, + 'default_name': opportunity.name} + return res + + def write(self, cr, uid, ids, vals, context = None): + if vals.get('stage_id') and not vals.get('probability'): + stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context) + if stage.on_change: + vals['probability'] = stage.probability + return super(crm_lead, self).write(cr, uid, ids, vals, context=context) + + def new_mail_send(self, cr, uid, ids, context = None): + """ + This function opens a window to compose an email, with the edi sale template message loaded by default + """ + if not len(ids) == 1: + raise AssertionError('This option should only be used for a single id at a time.') + ir_model_data = self.pool.get('ir.model.data') + try: + template_id = ir_model_data.get_object_reference(cr, uid, 'crm', 'email_template_opportunity_mail')[1] + except ValueError: + template_id = False + + try: + compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] + except ValueError: + compose_form_id = False + + context = context is None and {} + ctx = context.copy() + ctx.update({'default_model': 'crm.lead', + 'default_res_id': ids[0], + 'default_use_template': bool(template_id), + 'default_template_id': template_id, + 'default_composition_mode': 'comment'}) + return {'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'mail.compose.message', + 'views': [(compose_form_id, 'form')], + 'view_id': compose_form_id, + 'target': 'new', + 'context': ctx} + + def message_get_reply_to(self, cr, uid, ids, context = None): + """ Override to get the reply_to of the parent project. """ + return [ (lead.section_id.message_get_reply_to()[0] if lead.section_id else False) for lead in self.browse(cr, SUPERUSER_ID, ids, context=context) ] + + def message_get_suggested_recipients(self, cr, uid, ids, context = None): + recipients = super(crm_lead, self).message_get_suggested_recipients(cr, uid, ids, context=context) + try: + for lead in self.browse(cr, uid, ids, context=context): + if lead.partner_id: + self._message_add_suggested_recipient(cr, uid, recipients, lead, partner=lead.partner_id, reason=_('Customer')) + elif lead.email_from: + self._message_add_suggested_recipient(cr, uid, recipients, lead, email=lead.email_from, reason=_('Customer Email')) + + except (osv.except_osv, orm.except_orm): + pass + + return recipients + + def message_new(self, cr, uid, msg, custom_values = None, context = None): + """ Overrides mail_thread message_new that is called by the mailgateway + through message_process. + This override updates the document according to the email. + """ + if custom_values is None: + custom_values = {} + desc = html2plaintext(msg.get('body')) if msg.get('body') else '' + defaults = {'name': msg.get('subject') or _('No Subject'), + 'description': desc, + 'email_from': msg.get('from'), + 'email_cc': msg.get('cc'), + 'partner_id': msg.get('author_id', False), + 'user_id': False} + if msg.get('author_id'): + defaults.update(self.on_change_partner(cr, uid, None, msg.get('author_id'), context=context)['value']) + if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): + defaults['priority'] = msg.get('priority') + defaults.update(custom_values) + return super(crm_lead, self).message_new(cr, uid, msg, custom_values=defaults, context=context) + + def message_update(self, cr, uid, ids, msg, update_vals = None, context = None): + """ Overrides mail_thread message_update that is called by the mailgateway + through message_process. + This method updates the document according to the email. + """ + if isinstance(ids, (str, int, long)): + ids = [ids] + if update_vals is None: + update_vals = {} + if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): + update_vals['priority'] = msg.get('priority') + maps = {'cost': 'planned_cost', + 'revenue': 'planned_revenue', + 'probability': 'probability'} + for line in msg.get('body', '').split('\n'): + line = line.strip() + res = tools.command_re.match(line) + if res and maps.get(res.group(1).lower()): + key = maps.get(res.group(1).lower()) + update_vals[key] = res.group(2).lower() + + return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + + def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context = None): + phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] + if action == 'log': + prefix = 'Logged' + else: + prefix = 'Scheduled' + suffix = ' %s' % phonecall.description + message = _('%s a call for %s.%s') % (prefix, phonecall.date, suffix) + return self.message_post(cr, uid, ids, body=message, context=context) + + def log_meeting(self, cr, uid, ids, meeting_subject, meeting_date, duration, context = None): + if not duration: + duration = _('unknown') + else: + duration = str(duration) + message = _("Meeting scheduled at '%s'
    Subject: %s
    Duration: %s hour(s)") % (meeting_date, meeting_subject, duration) + return self.message_post(cr, uid, ids, body=message, context=context) + + def onchange_state(self, cr, uid, ids, state_id, context = None): + if state_id: + country_id = self.pool.get('res.country.state').browse(cr, uid, state_id, context).country_id.id + return {'value': {'country_id': country_id}} + return {} \ No newline at end of file diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index a477a8a4ae6d7..d319fcb347327 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -164,6 +164,7 @@ @@ -323,11 +324,11 @@ crm.lead - + - + @@ -391,7 +392,8 @@ type="action"/>
    +
    Total
    diff --git a/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py b/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py index c2d5167c17993..e9dbb2aed2ef7 100644 --- a/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py +++ b/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py @@ -1,63 +1,60 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -import time - -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -class hr_timesheet_current_open(osv.osv_memory): - _name = 'hr.timesheet.current.open' - _description = 'hr.timesheet.current.open' - - def open_timesheet(self, cr, uid, ids, context=None): - ts = self.pool.get('hr_timesheet_sheet.sheet') - if context is None: - context = {} - view_type = 'form,tree' - - user_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)], context=context) - if not len(user_ids): - raise osv.except_osv(_('Error!'), _('Please create an employee and associate it with this user.')) - ids = ts.search(cr, uid, [('user_id','=',uid),('state','in',('draft','new')),('date_from','<=',time.strftime('%Y-%m-%d')), ('date_to','>=',time.strftime('%Y-%m-%d'))], context=context) - - if len(ids) > 1: - view_type = 'tree,form' - domain = "[('id','in',["+','.join(map(str, ids))+"]),('user_id', '=', uid)]" - elif len(ids)==1: - domain = "[('user_id', '=', uid)]" - else: - domain = "[('user_id', '=', uid)]" - value = { - 'domain': domain, - 'name': _('Open Timesheet'), - 'view_type': 'form', - 'view_mode': view_type, - 'res_model': 'hr_timesheet_sheet.sheet', - 'view_id': False, - 'type': 'ir.actions.act_window' - } - if len(ids) == 1: - value['res_id'] = ids[0] - return value - -hr_timesheet_current_open() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class hr_timesheet_current_open(osv.osv_memory): + _name = 'hr.timesheet.current.open' + _description = 'hr.timesheet.current.open' + + def open_timesheet(self, cr, uid, ids, context = None): + ts = self.pool.get('hr_timesheet_sheet.sheet') + if context is None: + context = {} + view_type = 'form,tree' + user_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context) + if not len(user_ids): + raise osv.except_osv(_('Error!'), _('Please create an employee and associate it with this user.')) + ids = ts.search(cr, uid, [('user_id', '=', uid), + ('state', 'in', ('draft', 'new')), + ('date_from', '<=', time.strftime('%Y-%m-%d')), + ('date_to', '>=', time.strftime('%Y-%m-%d'))], context=context) + if len(ids) > 1: + view_type = 'tree,form' + domain = "[('id','in',[" + ','.join(map(str, ids)) + "]),('user_id', '=', uid)]" + elif len(ids) == 1: + domain = "[('user_id', '=', uid)]" + else: + domain = "[('user_id', '=', uid)]" + value = {'domain': domain, + 'name': _('Open Timesheet'), + 'view_type': 'form', + 'view_mode': view_type, + 'res_model': 'hr_timesheet_sheet.sheet', + 'view_id': False, + 'type': 'ir.actions.act_window'} + if len(ids) == 1: + value['res_id'] = ids[0] + return value + + +hr_timesheet_current_open() \ No newline at end of file diff --git a/addons/icsc_lt_baocao.py b/addons/icsc_lt_baocao.py new file mode 100644 index 0000000000000..cad3b6f108a4e --- /dev/null +++ b/addons/icsc_lt_baocao.py @@ -0,0 +1,780 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/icsc_lt_baocao/icsc_lt_baocao.py +from openerp.osv import osv, fields +from datetime import date +import datetime +import time +from datetime import timedelta +date1 = datetime.datetime.now() + datetime.timedelta(hours=7) + +class icsc_year(osv.osv): + _name = 'icsc.year' + _columns = {'name': fields.char('N\xc4\x83m', size=4, required=True)} + + +icsc_year() + +class icsc_month(osv.osv): + _name = 'icsc.month' + _columns = {'name': fields.char('Th\xc3\xa1ng', size=2, required=True)} + + +icsc_month() + +class icsc_baocao_hangngay(osv.osv): + _name = 'icsc.baocao.hangngay' + _order = 'id desc' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'nhom_kh': fields.many2one('res.partner.category', 'Nh\xc3\xb3m kh\xc3\xa1ch h\xc3\xa0ng'), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90V th\xe1\xbb\xb1c hi\xe1\xbb\x87n')} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_hangngay() + +class icsc_baocao_lenh_tonghop(osv.osv): + _name = 'icsc.baocao.lenh.tonghop' + _order = 'id desc' + _columns = {'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90V th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'nhom_kh': fields.many2one('res.partner.category', 'Nh\xc3\xb3m kh\xc3\xa1ch h\xc3\xa0ng'), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'kho_gui': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('cong_ty', 'Kho c\xc3\xb4ng ty'), + ('kho_ngoai_tt', 'Kho t\xe1\xba\xadp trung'), + ('kho_ngoai', 'Kho \xc4\x91\xe1\xba\xa1i l\xc3\xbd')], 'Kho g\xe1\xbb\xadi \xc4\x91\xe1\xba\xbfn', required=True)} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today, + 'kho_gui': 'all'} + + +icsc_baocao_lenh_tonghop() + +class icsc_baocao_tonlenh_mathang_khachhang(osv.osv): + _name = 'icsc.baocao.tonlenh.mathang.khachhang' + _columns = {'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90v th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'nhom_kh': fields.many2one('res.partner.category', 'Nh\xc3\xb3m kh\xc3\xa1ch h\xc3\xa0ng'), + 'date': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'kho_gui': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('cong_ty', 'Kho c\xc3\xb4ng ty'), + ('kho_ngoai_tt', 'Kho t\xe1\xba\xadp trung'), + ('kho_ngoai', 'Kho \xc4\x91\xe1\xba\xa1i l\xc3\xbd')], 'Kho g\xe1\xbb\xadi \xc4\x91\xe1\xba\xbfn', required=True)} + _defaults = {'date': fields.date.context_today, + 'kho_gui': 'all'} + + +icsc_baocao_tonlenh_mathang_khachhang() + +class icsc_baocao_sokelenh_guikho(osv.osv): + _name = 'icsc.baocao.sokelenh.guikho' + _columns = {'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)], required=False), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'hopdong': fields.many2one('icsc.hopdong.banhang', 'H\xe1\xbb\xa3p \xc4\x91\xe1\xbb\x93ng', required=True), + 'loai_hd': fields.many2one('icsc.hopdong.banhang.loai', 'H\xe1\xbb\xa3p \xc4\x91\xe1\xbb\x93ng')} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_sokelenh_guikho() + +class icsc_baocao_tonghop_phatsinh_khachhang(osv.osv): + _name = 'icsc.baocao.tonghop.phatsinh.khachhang' + _columns = {'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)], required=False), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'lenh_xuat': fields.many2one('icsc.lt.loai.xuat.hang', 'Lo\xe1\xba\xa1i l\xe1\xbb\x87nh xu\xe1\xba\xa5t'), + 'nhom_kh': fields.many2one('res.partner.category', 'Nh\xc3\xb3m kh\xc3\xa1ch h\xc3\xa0ng'), + 'categ_id': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m')} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today, + 'categ_id': 1} + + +icsc_baocao_tonghop_phatsinh_khachhang() + +class icsc_baocao_tonghop_phatsinh_sanpham(osv.osv): + _name = 'icsc.baocao.tonghop.phatsinh.sanpham' + _columns = {'categ_id': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m'), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True)} + _defaults = {'categ_id': 1, + 'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_tonghop_phatsinh_sanpham() + +class icsc_baocao_hopdong_banhang(osv.osv): + _name = 'icsc.baocao.hopdong.banhang' + _columns = {'loai_hop_dong': fields.many2one('icsc.hopdong.banhang.loai', 'Lo\xe1\xba\xa1i h\xe1\xbb\xa3p \xc4\x91\xe1\xbb\x93ng'), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True)} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_hopdong_banhang() +icsc_baocao_hopdong_banhang() + +class icsc_baocao_ketqua_tieuthu(osv.osv): + _name = 'icsc.baocao.ketqua.tieuthu' + _order = 'id desc' + _columns = {'categ_id': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m', required=True), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90V th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'categ_id': 1, + 'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_ketqua_tieuthu() + +class icsc_baocao_banhang(osv.osv): + _name = 'icsc.baocao.banhang' + _columns = {'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)], required=False), + 'categ_id': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m'), + 'nhom_kh': fields.many2one('res.partner.category', 'Nh\xc3\xb3m kh\xc3\xa1ch h\xc3\xa0ng'), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90v th\xe1\xbb\xb1c hi\xe1\xbb\x87n')} + _defaults = {'categ_id': 1, + 'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_banhang() +icsc_baocao_banhang() + +class icsc_baocao_danhgia_tieuthu(osv.osv): + _name = 'icsc.baocao.danhgia.tieuthu' + _order = 'id desc' + _columns = {'tinh': fields.many2one('res.country.state', 'T\xe1\xbb\x89nh', domain=[('country_id', '=', 243)]), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90V th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'loai_hd': fields.many2one('icsc.hopdong.banhang.loai', 'Lo\xe1\xba\xa1i h\xe1\xbb\xa3p \xc4\x91\xe1\xbb\x93ng'), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)])} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_danhgia_tieuthu() + +class icsc_baocao_pxk_kiemvcnoibo(osv.osv): + _name = 'icsc.baocao.pxk.kiemvcnoibo' + _columns = {'ngay_bd_phieu': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=True), + 'ngay_kt_phieu': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=True), + 'tinh': fields.many2one('res.country.state', 'T\xe1\xbb\x89nh', domain=[('country_id', '=', 243)]), + 'categ': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m'), + 'loai_xuat_hang': fields.many2one('icsc.lt.loai.xuat.hang', 'Lo\xe1\xba\xa1i xu\xe1\xba\xa5t h\xc3\xa0ng'), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)], required=False), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90v th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'lenh_xuat': fields.many2one('sale.order', 'L\xe1\xbb\x87nh xu\xe1\xba\xa5t'), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'categ': 1, + 'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_pxk_kiemvcnoibo() + +class icsc_baocao_hoadon_banhang(osv.osv): + _name = 'icsc.baocao.hoadon.banhang' + _columns = {'loai_hoadon': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), ('thongthuong', 'Th\xc3\xb4ng th\xc6\xb0\xe1\xbb\x9dng'), ('noibo', 'N\xe1\xbb\x99i b\xe1\xbb\x99')], 'Lo\xe1\xba\xa1i h\xc3\xb3a \xc4\x91\xc6\xa1n', required=True), + 'categ_id': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m', required=True), + 'type_kho': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('kho_congty', 'Kho c\xc3\xb4ng ty'), + ('kho_taptrung', 'Kho t\xe1\xba\xadp trung'), + ('kho_daily', 'Kho \xc4\x91\xe1\xba\xa1i l\xc3\xbd')], 'Lo\xe1\xba\xa1i h\xc3\xacnh kho', required=True), + 'tungay': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=True), + 'denngay': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=True), + 'loai_xuat_hang': fields.many2one('icsc.lt.loai.xuat.hang', 'Lo\xe1\xba\xa1i xu\xe1\xba\xa5t h\xc3\xa0ng'), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)], required=False), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90v th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'nhom_kh': fields.many2one('res.partner.category', 'Nh\xc3\xb3m kh\xc3\xa1ch h\xc3\xa0ng'), + 'product_id': fields.many2one('product.product', 'S\xe1\xba\xa3n ph\xe1\xba\xa9m')} + _defaults = {'loai_hoadon': 'all', + 'type_kho': 'all', + 'categ_id': 1, + 'tungay': fields.date.context_today, + 'denngay': fields.date.context_today} + + +icsc_baocao_hoadon_banhang() + +class icsc_baocao_luongtieuthu(osv.osv): + _name = 'icsc.baocao.luongtieuthu' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'district': fields.many2one('res.country.district', 'Huy\xe1\xbb\x87n', required=False, domain="[('state_id','=',state)]"), + 'state': fields.many2one('res.country.state', 'T\xe1\xbb\x89nh', required=False), + 'nam': fields.char('N\xc4\x83m xu\xe1\xba\xa5t', required=True), + 'thang': fields.many2one('icsc.month', 'T\xe1\xbb\xab th\xc3\xa1ng', required=True), + 'thang2': fields.many2one('icsc.month', '\xc4\x90\xe1\xba\xbfn th\xc3\xa1ng', required=True)} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today, + 'nam': datetime.date.today().year} + + +icsc_baocao_luongtieuthu() + +class icsc_baocao_kehoach_phanbon(osv.osv): + _name = 'icsc.baocao.kehoach.phanbon' + _order = 'id desc' + _columns = {'ngay': fields.date('Ch\xe1\xbb\x8dn ng\xc3\xa0y', required=True), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_kehoach_phanbon() + +class icsc_baocao_kehoach_phanbon_canam(osv.osv): + _name = 'icsc.baocao.kehoach.phanbon.canam' + _order = 'id desc' + _columns = {'denngay': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'loai_khvc': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('vat', 'VAT'), + ('gui_ban', 'G\xe1\xbb\xadi h\xc3\xa0ng c\xc3\xb3 \xc4\x91\xe1\xba\xa3m b\xe1\xba\xa3o'), + ('kho_tap_trung', 'G\xe1\xbb\xadi kho t\xe1\xba\xadp trung'), + ('kho_dai_ly', 'H\xc3\xa0ng nguy\xc3\xaan li\xe1\xbb\x87u gia c\xc3\xb4ng')], 'Lo\xe1\xba\xa1i KHVC', required=True), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'loai_khvc': 'all', + 'denngay': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_kehoach_phanbon_canam() + +class icsc_baocao_ketquavc(osv.osv): + _name = 'icsc.baocao.ketquavc' + _order = 'id desc' + _columns = {'name': fields.char('N\xc4\x83m', required=True), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'name': datetime.date.today().year, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_ketquavc() + +class icsc_baocao_hopdong_vanchuyen(osv.osv): + _name = 'icsc.baocao.hopdong.vanchuyen' + _order = 'id desc' + _columns = {'ngay_bd_hd': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u h\xe1\xbb\xa3p \xc4\x91\xe1\xbb\x93ng', required=True), + 'ngay_kt_hd': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac h\xe1\xbb\xa3p \xc4\x91\xe1\xbb\x93ng', required=True), + 'donvi_vc': fields.many2one('res.partner', 'Cty v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay_bd_hd': fields.date.context_today, + 'ngay_kt_hd': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_hopdong_vanchuyen() + +class icsc_baocao_hopdong_cuocvanchuyen(osv.osv): + _name = 'icsc.baocao.hopdong.cuocvanchuyen' + _order = 'id desc' + _columns = {'ngay': fields.date('Ng\xc3\xa0y', required=True), + 'donvi_vc': fields.many2one('res.partner', 'Cty v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)], required=True), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_hopdong_vanchuyen() + +class icsc_baocao_phieu_vanchuyen(osv.osv): + _name = 'icsc.baocao.phieu.vanchuyen' + _order = 'id desc' + _columns = {'loai_phieu': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), ('thongthuong', 'Th\xc3\xb4ng th\xc6\xb0\xe1\xbb\x9dng'), ('noibo', 'N\xe1\xbb\x99i b\xe1\xbb\x99')], 'Lo\xe1\xba\xa1i phi\xe1\xba\xbfu v\xe1\xba\xadn chuy\xe1\xbb\x83n', required=True), + 'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=False), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=False), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'loai_khvc': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('vat', 'VAT'), + ('gui_ban', 'G\xe1\xbb\xadi h\xc3\xa0ng c\xc3\xb3 \xc4\x91\xe1\xba\xa3m b\xe1\xba\xa3o'), + ('kho_tap_trung', 'G\xe1\xbb\xadi kho t\xe1\xba\xadp trung'), + ('kho_dai_ly', 'H\xc3\xa0ng nguy\xc3\xaan li\xe1\xbb\x87u gia c\xc3\xb4ng')], 'Lo\xe1\xba\xa1i KHVC', required=True), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90v th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'kho_xuat': fields.many2one('res.partner', 'Kho xu\xe1\xba\xa5t', domain=['|', + ('is_trungchuyen', '=', True), + ('kho', '=', True), + ('is_diadiem', '!=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'loai_phieu': 'all', + 'loai_khvc': 'all', + 'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_phieu_vanchuyen() + +class icsc_baocao_phieu_vanchuyen_thuybo(osv.osv): + _name = 'icsc.baocao.phieu.vanchuyen.thuybo' + _order = 'id desc' + _columns = {'loai_khvc': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('vat', 'VAT'), + ('gui_ban', 'G\xe1\xbb\xadi h\xc3\xa0ng c\xc3\xb3 \xc4\x91\xe1\xba\xa3m b\xe1\xba\xa3o'), + ('kho_tap_trung', 'G\xe1\xbb\xadi kho t\xe1\xba\xadp trung'), + ('kho_dai_ly', 'H\xc3\xa0ng nguy\xc3\xaan li\xe1\xbb\x87u gia c\xc3\xb4ng')], 'Lo\xe1\xba\xa1i KHVC', required=True), + 'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=False), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=False), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90v th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'state_id': fields.many2one('res.country.state', 'Khu v\xe1\xbb\xb1c nh\xe1\xba\xadp'), + 'diachi_giao': fields.many2one('res.partner', '\xc4\x90\xe1\xbb\x8ba ch\xe1\xbb\x89 giao', domain=[('is_diadiem', '=', True), ('active', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'loai_khvc': 'all', + 'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_phieu_vanchuyen_thuybo() + +class icsc_baocao_kho(osv.osv): + _name = 'icsc.baocao.kho' + _order = 'id desc' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'chang_khvc': fields.selection([('chang_1', 'Ch\xe1\xba\xb7ng 1 & Kh\xc3\xb4ng trung chuy\xe1\xbb\x83n'), ('chang_2', 'Ch\xe1\xba\xb7ng 2')], 'Ch\xe1\xba\xb7ng KHVC', required=True), + 'loai_khvc': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('vat', 'VAT'), + ('gui_ban', 'G\xe1\xbb\xadi h\xc3\xa0ng c\xc3\xb3 \xc4\x91\xe1\xba\xa3m b\xe1\xba\xa3o'), + ('kho_tap_trung', 'G\xe1\xbb\xadi kho t\xe1\xba\xadp trung'), + ('kho_dai_ly', 'H\xc3\xa0ng nguy\xc3\xaan li\xe1\xbb\x87u gia c\xc3\xb4ng')], 'Lo\xe1\xba\xa1i KHVC', required=True), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'loai_khvc': 'all', + 'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_kho() + +class icsc_baocao_tonghop_vanchuyen(osv.osv): + _name = 'icsc.baocao.tonghop.vanchuyen' + _order = 'id desc' + _columns = {'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=True), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=True), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'loai_khvc': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('vat', 'VAT'), + ('gui_ban', 'G\xe1\xbb\xadi h\xc3\xa0ng c\xc3\xb3 \xc4\x91\xe1\xba\xa3m b\xe1\xba\xa3o'), + ('kho_tap_trung', 'G\xe1\xbb\xadi kho t\xe1\xba\xadp trung'), + ('kho_dai_ly', 'H\xc3\xa0ng nguy\xc3\xaan li\xe1\xbb\x87u gia c\xc3\xb4ng')], 'Lo\xe1\xba\xa1i KHVC', required=True), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'loai_khvc': 'all', + 'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_tonghop_vanchuyen() + +class icsc_baocao_khoiluong_giaonhan(osv.osv): + _name = 'icsc.baocao.khoiluong.giaonhan' + _order = 'id desc' + _columns = {'dv_vanchuyen': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)], required=True), + 'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'lan_bc': fields.char('L\xe1\xba\xa7n b\xc3\xa1o c\xc3\xa1o', required=True), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_khoiluong_giaonhan() + +class icsc_baocao_khoiluong_tiencuoc_giaonhan(osv.osv): + _name = 'icsc.baocao.khoiluong.tiencuoc.giaonhan' + _order = 'id desc' + _columns = {'dv_vanchuyen': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'name': fields.char('N\xc4\x83m', required=True), + 'date': fields.date('Ng\xc3\xa0y'), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'name': datetime.date.today().year, + 'date': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_khoiluong_tiencuoc_giaonhan() + +class icsc_baocao_denghithanhtoan(osv.osv): + _name = 'icsc.baocao.denghithanhtoan' + _order = 'id desc' + _columns = {'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=True), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=True), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_denghithanhtoan() + +class icsc_thanhtoan_vanchuyen(osv.osv): + _name = 'icsc.thanhtoan.vanchuyen' + _order = 'id desc' + _columns = {'dv_vanchuyen': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)], required=True), + 'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'chitiet_ids': fields.one2many('icsc.thanhtoan.vanchuyen.chitiet', 'phieu_id', 'Chi ti\xe1\xba\xbft'), + 'name': fields.char('M\xc3\xb4 t\xe1\xba\xa3'), + 'user_id': fields.many2one('res.users', 'Ng\xc6\xb0\xe1\xbb\x9di c\xe1\xba\xadp nh\xe1\xba\xadt')} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + def _check_category(self, cr, uid, orders_line_list, category_id): + if len(orders_line_list) > 0: + for line_id in orders_line_list: + orderline = self.pool.get('icsc.thanhtoan.vanchuyen.chitiet').browse(cr, uid, line_id) + prod_id = orderline.category_id.id + if prod_id == category_id: + return line_id + + return False + + def merge_move_line(self, cr, uid, order_id, context = None): + picking_obj = self.browse(cr, uid, order_id, context) + if picking_obj: + move_lines = picking_obj.chitiet_ids + orders_line_list = [] + for line in move_lines: + if line.category_id: + category_id = line.category_id.id + product_qty2 = line.so_phieu + line_get = self._check_category(cr, uid, orders_line_list, category_id) + if line_get: + move1 = self.pool.get('icsc.thanhtoan.vanchuyen.chitiet').browse(cr, uid, line_get) + product_qty1 = move1.so_phieu + self.pool.get('icsc.thanhtoan.vanchuyen.chitiet').write(cr, uid, line_get, {'so_phieu': product_qty1 + product_qty2}) + query = 'delete from icsc_thanhtoan_vanchuyen_chitiet where id=%s' + cr.execute(query, (line.id,)) + else: + orders_line_list.append(line.id) + + return True + + def action_load_data(self, cr, uid, ids, context = None): + chitiet_pool = self.pool.get('icsc.thanhtoan.vanchuyen.chitiet') + pvc_id = [] + pvc_id_tt = [] + for data in self.browse(cr, uid, ids, context=None): + cr.execute('delete from icsc_thanhtoan_vanchuyen_chitiet where phieu_id=%s' % data.id) + dv_vanchuyen = data.dv_vanchuyen.id + ngay_bd = data.ngay_bd + ngay_kt = data.ngay_kt + query = "\n select id from\n (\n select distinct pc.id as id, \n sum(case when COALESCE(pvc.tung_phan,FALSE)=true then ct.kl_dangvc_dukien else ct.kl_vc end) as slps\n from icsc_phieu_vanchuyen pvc\n left join icsc_phieu_vanchuyen_chitiet ct on ct.phieu_id = pvc.id\n left join product_product p on ct.product_id = p.id\n left join product_template pt on p.product_tmpl_id = pt.id\n left join product_category pc on pt.categ_id = pc.id\n left join res_partner rp on rp.id = pvc.congty_vc\n where pvc.state not in ('draft','cancel') and ct.state not in ('draft','cancel')\n and pvc.ngay_vc between '%s'::date and '%s'::date and rp.id = %s\n group by pc.id \n ) a order by slps desc \n " % (ngay_bd, ngay_kt, dv_vanchuyen) + cr.execute(query) + for line in cr.dictfetchall(): + nhomsp = False + category_id = line['id'] + count = ps = tt = 0 + query_ps = "select pvc.id as pvc_id\n from icsc_phieu_vanchuyen pvc\n left join icsc_phieu_vanchuyen_chitiet ct on ct.phieu_id = pvc.id\n left join product_product p on ct.product_id = p.id\n left join product_template pt on p.product_tmpl_id = pt.id\n left join product_category pc on pt.categ_id = pc.id\n left join res_partner rp on rp.id = pvc.congty_vc\n where pvc.state not in ('draft','cancel') and ct.state not in ('draft','cancel')\n and pvc.ngay_vc between '%s'::date and '%s'::date and rp.id = %s and pc.id=%s \n " % (ngay_bd, + ngay_kt, + dv_vanchuyen, + category_id) + cr.execute(query_ps) + for item_ps in cr.dictfetchall(): + if item_ps['pvc_id'] not in pvc_id: + ps += 1 + pvc_id.append(item_ps['pvc_id']) + + query_tt = "select pvc.id as pvc_id\n from icsc_denghithanhtoan_vanchuyen dn\n left join icsc_denghithanhtoan_vanchuyen_chitiet dnt on dnt.phieu_id = dn.id\n left join icsc_phieu_vanchuyen pvc on pvc.id = dnt.so_phieu_vc\n left join icsc_phieu_vanchuyen_chitiet ct on ct.phieu_id = pvc.id\n left join product_product p on ct.product_id = p.id\n left join product_template pt on p.product_tmpl_id = pt.id\n left join product_category pc on pt.categ_id = pc.id\n left join res_partner rp on rp.id = pvc.congty_vc\n where dn.state not in ('draft','cancel') and pvc.state not in ('draft','cancel')\n and dn.thanhtoan_tungay >= '%s'::date and dn.thanhtoan_denngay <= '%s'::date\n and rp.id = %s and pc.id = %s \n " % (ngay_bd, + ngay_kt, + dv_vanchuyen, + category_id) + cr.execute(query_tt) + for item_tt in cr.dictfetchall(): + if item_tt['pvc_id'] not in pvc_id_tt: + tt += 1 + pvc_id_tt.append(item_tt['pvc_id']) + + vals = {'category_id': category_id, + 'so_phieu_ps': ps, + 'so_phieu_tt': tt, + 'so_phieu_ton': ps - tt, + 'phieu_id': data.id} + chitiet_pool.create(cr, uid, vals, context=context) + + self.write(cr, uid, ids, {'user_id': uid}, context) + return True + + def create(self, cr, uid, vals, context = None): + new_id = super(icsc_thanhtoan_vanchuyen, self).create(cr, uid, vals, context) + self.merge_move_line(cr, uid, new_id, context=None) + self.action_load_data(cr, uid, [new_id], context=None) + return new_id + + def write(self, cr, uid, ids, vals, context = None): + res = super(icsc_thanhtoan_vanchuyen, self).write(cr, uid, ids, vals, context=context) + if ids: + try: + if ids[0]: + self.merge_move_line(cr, uid, ids[0], context=context) + self.action_load_data(cr, uid, ids[0], context=context) + except: + return res + + return res + + _defaults = {'name': 'B\xe1\xba\xa2NG THANH TO\xc3\x81N V\xc3\x80 X\xc3\x81C NH\xe1\xba\xacN K.L\xc6\xaf\xe1\xbb\xa2NG, \xc4\x90.GI\xc3\x81 V\xc3\x80 C\xc6\xaf\xe1\xbb\x9aC PH\xc3\x8d V\xe1\xba\xacN CHUY\xe1\xbb\x82N H\xc3\x80NG H\xc3\x93A THU\xc3\x8a NGO\xc3\x80I'} + + +icsc_thanhtoan_vanchuyen() + +class icsc_thanhtoan_vanchuyen_chitiet(osv.osv): + _name = 'icsc.thanhtoan.vanchuyen.chitiet' + _order = 'id desc' + _columns = {'category_id': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m', ondelete='cascade'), + 'so_phieu_ps': fields.integer('Phi\xe1\xba\xbfu ph\xc3\xa1t sinh'), + 'so_phieu_tt': fields.integer('Phi\xe1\xba\xbfu thanh to\xc3\xa1n'), + 'so_phieu_ton': fields.integer('Phi\xe1\xba\xbfu t\xe1\xbb\x93n'), + 'phieu_id': fields.many2one('icsc.thanhtoan.vanchuyen', 'Thanh to\xc3\xa1n', ondelete='cascade')} + + +icsc_thanhtoan_vanchuyen() + +class icsc_tongtien_thanhtoan_vanchuyen(osv.osv): + _name = 'icsc.tongtien.thanhtoan.vanchuyen' + _order = 'id desc' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_tongtien_thanhtoan_vanchuyen() + +class icsc_baocao_guihang_duongsat(osv.osv): + _name = 'icsc.baocao.guihang.duongsat' + _columns = {'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_guihang_duongsat() + +class icsc_baocao_kehoach_vanchuyen(osv.osv): + _name = 'icsc.baocao.kehoach.vanchuyen' + _order = 'id desc' + _columns = {'ngay_bd_kh': fields.date('Ng\xc3\xa0y v\xe1\xba\xadn chuy\xe1\xbb\x83n', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=True), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay_bd_kh': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_kehoach_vanchuyen() + +class icsc_baocao_uyquyen_gioithieu(osv.osv): + _name = 'icsc.baocao.uyquyen.gioithieu' + _order = 'id desc' + _columns = {'nam': fields.char('N\xc4\x83m', required=True), + 'donvi_vc': fields.many2one('res.partner', 'Cty v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)], required=True), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'nam': datetime.date.today().year, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_uyquyen_gioithieu() + +class icsc_baocao_tonghop_kho(osv.osv): + _name = 'icsc.baocao.tonghop.kho' + _order = 'id desc' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'loai_kho': fields.selection([('kho_daily', 'Kho \xc4\x91\xe1\xba\xa1i l\xc3\xbd'), + ('kho_congty', 'Kho c\xc3\xb4ng ty'), + ('kho_taptrung', 'Kho t\xe1\xba\xadp trung'), + ('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3')], 'Lo\xe1\xba\xa1i kho', required=True), + 'vi_tri': fields.many2one('stock.location', '\xc4\x90\xe1\xbb\x8ba di\xe1\xbb\x83m kh\xc3\xa1ch h\xc3\xa0ng', domain=[('active', '=', True)]), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'categ_id': fields.many2one('product.category', 'Lo\xe1\xba\xa1i s\xe1\xba\xa3n ph\xe1\xba\xa9m', required=True), + 'state_id': fields.many2one('res.country.state', 'T\xe1\xbb\x89nh')} + _defaults = {'loai_kho': 'all', + 'categ_id': 1, + 'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_tonghop_kho() + +class icsc_baocao_tonghop_phatsinh(osv.osv): + _name = 'icsc.baocao.tonghop.phatsinh' + _order = 'id desc' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True), + 'loai_kho': fields.selection([('kho_daily', 'Kho \xc4\x91\xe1\xba\xa1i l\xc3\xbd'), + ('kho_congty', 'Kho c\xc3\xb4ng ty'), + ('kho_taptrung', 'Kho t\xe1\xba\xadp trung'), + ('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3')], 'Lo\xe1\xba\xa1i kho', required=True), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)])} + _defaults = {'loai_kho': 'all', + 'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_tonghop_phatsinh() + +class icsc_baocao_xuatkho(osv.osv): + _name = 'icsc.baocao.xuatkho' + _order = 'id desc' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', required=True)} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today} + + +icsc_baocao_xuatkho() + +class icsc_baocao_xuatkho2(osv.osv): + _name = 'icsc.baocao.xuatkho2' + _order = 'id desc' + _columns = {'nam': fields.char('N\xc4\x83m xu\xe1\xba\xa5t'), + 'thang': fields.many2one('icsc.month', 'Th\xc3\xa1ng xu\xe1\xba\xa5t'), + 'loai_kho': fields.selection([('kho_daily', 'Kho \xc4\x91\xe1\xba\xa1i l\xc3\xbd'), + ('kho_congty', 'Kho c\xc3\xb4ng ty'), + ('kho_taptrung', 'Kho t\xe1\xba\xadp trung'), + ('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3')], 'Lo\xe1\xba\xa1i kho', required=True)} + _defaults = {'loai_kho': 'all', + 'nam': datetime.date.today().year} + + +icsc_baocao_xuatkho2() + +class icsc_baocao_phatsinh_hanggui(osv.osv): + _name = 'icsc.baocao.phatsinh.hanggui' + _order = 'id desc' + _columns = {'kho_xuat': fields.many2one('stock.location', 'Kho xu\xe1\xba\xa5t', required=True), + 'kho_nhap': fields.many2one('stock.location', 'Kho nh\xe1\xba\xadp', required=True, domain=[('location_id', '=', False)]), + 'khach_hang': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)], required=True), + 'nam': fields.char('N\xc4\x83m', required=True)} + _defaults = {'nam': datetime.date.today().year} + + +icsc_baocao_phatsinh_hanggui() + +class icsc_baocao_bienban_kiemke(osv.osv): + _name = 'icsc.baocao.bienban.kiemke' + _order = 'id desc' + _columns = {'thang': fields.many2one('icsc.month', 'Th\xc3\xa1ng b\xc3\xa1o c\xc3\xa1o', required=True), + 'nam': fields.char('N\xc4\x83m', required=True), + 'vi_tri': fields.many2one('stock.location', 'Kho'), + 'partner_id': fields.many2one('res.partner', 'Kh\xc3\xa1ch h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'loai_kho': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('kho_daily', 'Kho \xc4\x91\xe1\xba\xa1i l\xc3\xbd'), + ('kho_congty', 'Kho c\xc3\xb4ng ty'), + ('kho_taptrung', 'Kho t\xe1\xba\xadp trung')], 'Lo\xe1\xba\xa1i kho')} + _defaults = {'loai_kho': 'all', + 'nam': datetime.date.today().year} + + +icsc_baocao_bienban_kiemke() + +class icsc_baocao_phieuthu_tienthu(osv.osv): + _name = 'icsc.baocao.phieuthu.tienthu' + _columns = {'ngay_batdau': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=True), + 'ngay_kethuc': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=True), + 'ngan_hang': fields.many2one('account.journal', 'Ng\xc3\xa2n h\xc3\xa0ng', required=True), + 'bo_phan': fields.selection([('ban_hang', 'B\xc3\xa1n h\xc3\xa0ng'), ('ke_toan', 'K\xe1\xba\xbf to\xc3\xa1n')], 'B\xe1\xbb\x99 ph\xe1\xba\xadn', required=True)} + _defaults = {'ngay_batdau': fields.date.context_today, + 'ngay_batdau': fields.date.context_today} + + +icsc_baocao_phieuthu_tienthu() + +class icsc_baocao_bangke_phieu(osv.osv): + _name = 'icsc.baocao.bangke.phieu' + _order = 'id desc' + _columns = {'ngay_bd': fields.date('T\xe1\xbb\xab ng\xc3\xa0y', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=True), + 'ngay_kt': fields.date('\xc4\x90\xe1\xba\xbfn ng\xc3\xa0y', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=True), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90V th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'ngay_bd': fields.date.context_today, + 'ngay_kt': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_phieuthu_tienthu() + +class icsc_baocao_thongke_khoiluong(osv.osv): + _name = 'icsc.baocao.thongke.khioiluong' + _order = 'id desc' + _columns = {'loai_khvc': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('vat', 'VAT'), + ('gui_ban', 'G\xe1\xbb\xadi h\xc3\xa0ng c\xc3\xb3 \xc4\x91\xe1\xba\xa3m b\xe1\xba\xa3o'), + ('kho_tap_trung', 'G\xe1\xbb\xadi kho t\xe1\xba\xadp trung trung'), + ('kho_dai_ly', 'H\xc3\xa0ng nguy\xc3\xaan li\xe1\xbb\x87u gia c\xc3\xb4ng')], 'Lo\xe1\xba\xa1i KHVC', required=True), + 'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=False), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=False), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True)]), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90V th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'state_id': fields.many2one('res.country.state', 'Khu v\xe1\xbb\xb1c nh\xe1\xba\xadp'), + 'diachi_giao': fields.many2one('res.partner', '\xc4\x90\xe1\xbb\x8ba ch\xe1\xbb\x89 giao', domain=[('is_diadiem', '=', True), ('active', '=', True)]), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'loai_khvc': 'all', + 'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_thongke_khoiluong() + +class icsc_baocao_kl_kvkn(osv.osv): + _name = 'icsc.baocao.kl.kvkn' + _order = 'id desc' + _columns = {'loai_khvc': fields.selection([('all', 'T\xe1\xba\xa5t c\xe1\xba\xa3'), + ('vat', 'VAT'), + ('gui_ban', 'G\xe1\xbb\xadi h\xc3\xa0ng c\xc3\xb3 \xc4\x91\xe1\xba\xa3m b\xe1\xba\xa3o'), + ('kho_tap_trung', 'G\xe1\xbb\xadi kho t\xe1\xba\xadp trung'), + ('kho_dai_ly', 'H\xc3\xa0ng nguy\xc3\xaan li\xe1\xbb\x87u gia c\xc3\xb4ng')], 'Loo\xe1\xba\xa1i KHVC', required=True), + 'ngay_bd_phieu': fields.date('Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', help='Ng\xc3\xa0y b\xe1\xba\xaft \xc4\x91\xe1\xba\xa7u', required=False), + 'ngay_kt_phieu': fields.date('Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', help='Ng\xc3\xa0y k\xe1\xba\xbft th\xc3\xbac', required=False), + 'donvi_vc': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b v\xe1\xba\xadn chuy\xe1\xbb\x83n', domain=[('supplier', '=', True), ('active', '=', True)]), + 'donvi_nh': fields.many2one('res.partner', '\xc4\x90\xc6\xa1n v\xe1\xbb\x8b nh\xe1\xba\xadn h\xc3\xa0ng', domain=[('customer', '=', True), ('active', '=', True)]), + 'dv_thuchien': fields.many2one('sale.shop', '\xc4\x90V th\xe1\xbb\xb1c hi\xe1\xbb\x87n'), + 'state_id': fields.many2one('res.country.state', 'Khu v\xe1\xbb\xb1c nh\xe1\xba\xadp'), + 'diachi_giao': fields.many2one('res.partner', '\xc4\x90\xe1\xbb\x8ba ch\xe1\xbb\x89 giao', domain=[('is_diadiem', '=', True), ('active', '=', True)]), + 'tramkiemsoat': fields.many2one('res.country.tramkiemsoat', 'Tr\xe1\xba\xa1m ki\xe1\xbb\x83m so\xc3\xa1t'), + 'gio_bc': fields.datetime('Th\xe1\xbb\x9di gian xem b\xc3\xa1o c\xc3\xa1o', required=True, readonly=True)} + _defaults = {'loai_khvc': 'all', + 'ngay_bd_phieu': fields.date.context_today, + 'ngay_kt_phieu': fields.date.context_today, + 'gio_bc': lambda self, cr, uid, context = {}: context.get('gio_bc', time.strftime('%Y-%m-%d %H:%M:%S'))} + + +icsc_baocao_kl_kvkn() \ No newline at end of file diff --git a/addons/icsc_lt_baocao_view.xml b/addons/icsc_lt_baocao_view.xml new file mode 100644 index 0000000000000..d8f7edf4059f0 --- /dev/null +++ b/addons/icsc_lt_baocao_view.xml @@ -0,0 +1,875 @@ + + + + + + icsc.baocao.hangngay.tree + icsc.baocao.hangngay + + + + + + + + + + + + 1.11 BC LXH hằng ngày + icsc.baocao.hangngay + form + tree,form + + + + icsc.baocao.banhang.tree + icsc.baocao.banhang + + + + + + + + + + + + + 1.12 BC bán hàng + icsc.baocao.banhang + form + tree,form + + + + icsc.baocao.hoadon.banhang.tree + icsc.baocao.hoadon.banhang + + + + + + + + + + + + + + + + 1.13 Bảng kê hoá đơn bán hàng + icsc.baocao.hoadon.banhang + form + tree,form + + + + icsc.baocao.lenh.tonghop.tree + icsc.baocao.lenh.tonghop + + + + + + + + + + + + + 1.2 BC xuất tồn lệnh đơn vị + icsc.baocao.lenh.tonghop + form + tree,form + + + + icsc.baocao.tonlenh.mathang.khachhang.tree + icsc.baocao.tonlenh.mathang.khachhang + + + + + + + + + + + + 1.3 BC tồn lệnh chi tiết MH-KH + icsc.baocao.tonlenh.mathang.khachhang + form + tree,form + + + + icsc.baocao.sokelenh.guikho.tree + icsc.baocao.sokelenh.guikho + + + + + + + + + + 1.4 BC sổ kê lệnh theo HĐ + icsc.baocao.sokelenh.guikho + form + tree,form + + + + icsc.baocao.tonghop.phatsinh.khachhang.tree + icsc.baocao.tonghop.phatsinh.khachhang + + + + + + + + + + + + + 1.51 TH phát sinh theo khách hàng + icsc.baocao.tonghop.phatsinh.khachhang + form + tree,form + + + + icsc.baocao.tonghop.phatsinh.sanpham.tree + icsc.baocao.tonghop.phatsinh.sanpham + + + + + + + + + + 1.52 TH phát sinh theo sản phẩm + icsc.baocao.tonghop.phatsinh.sanpham + form + tree,form + + + + icsc.baocao.hopdong.banhang.tree + icsc.baocao.hopdong.banhang + + + + + + + + + + 1.6 TH hợp đồng bán hàng + icsc.baocao.hopdong.banhang + form + tree,form + + + + icsc.baocao.ketqua.tieuthu.tree + icsc.baocao.ketqua.tieuthu + + + + + + + + + + + + 1.71 BC kết quả tiêu thụ + icsc.baocao.ketqua.tieuthu + form + tree,form + + + + icsc.baocao.danhgia.tieuthu.tree + icsc.baocao.danhgia.tieuthu + + + + + + + + + + + + + 1.72 Đánh giá kết quả tiêu thụ + icsc.baocao.danhgia.tieuthu + form + tree,form + + + + icsc.baocao.pxk.kiemvcnoibo.tree + icsc.baocao.pxk.kiemvcnoibo + + + + + + + + + + + + + + + + 1.8 Thống kê PXK kiêm VCNB + icsc.baocao.pxk.kiemvcnoibo + form + tree,form + + + + icsc.baocao.luongtieuthu.tree + icsc.baocao.luongtieuthu + + + + + + + + + + + + 1.9 BC sản lượng tiêu thụ + icsc.baocao.luongtieuthu + form + tree,form + + + + icsc.baocao.kehoach.phanbon.tree + icsc.baocao.kehoach.phanbon + + + + + + + + + + + 2.0 Kế hoạch vận chuyển + icsc.baocao.kehoach.phanbon + form + tree,form + + + + icsc.baocao.kehoach.phanbon.canam.tree + icsc.baocao.kehoach.phanbon.canam + + + + + + + + + + + + 2.11 TK kết quả vận chuyển + icsc.baocao.kehoach.phanbon.canam + form + tree,form + + + + icsc.baocao.ketquavc.tree + icsc.baocao.ketquavc + + + + + + + + + + + 2.12 Kết quả VC - ĐVNH + icsc.baocao.ketquavc + form + tree,form + + + + icsc.baocao.hopdong.vanchuyen.tree + icsc.baocao.hopdong.vanchuyen + + + + + + + + + + + 2.21 Hợp đồng vận chuyển + icsc.baocao.hopdong.vanchuyen + form + tree,form + + + + icsc.baocao.hopdong.cuocvanchuyen.tree + icsc.baocao.hopdong.cuocvanchuyen + + + + + + + + + + 2.22 TB thay đổi đơn giá cước + icsc.baocao.hopdong.cuocvanchuyen + form + tree,form + + + + icsc.baocao.phieu.vanchuyen.tree + icsc.baocao.phieu.vanchuyen + + + + + + + + + + + + + + + + 2.31 Thống kê phiếu vận chuyển + icsc.baocao.phieu.vanchuyen + form + tree,form + + + + + icsc.baocao.phieu.vanchuyen.thuybo.tree + icsc.baocao.phieu.vanchuyen.thuybo + + + + + + + + + + + + + + + + 2.32 TH vận chuyển thủy-bộ + icsc.baocao.phieu.vanchuyen.thuybo + form + tree,form + + + + icsc.baocao.kho.tree + icsc.baocao.kho + + + + + + + + + + + + + 2.4 BC tồn lệnh-kế hoạch + icsc.baocao.kho + form + tree,form + + + + icsc.baocao.tonghop.vanchuyen.tree + icsc.baocao.tonghop.vanchuyen + + + + + + + + + + + + + 2.51 Báo cáo khối lượng VC + icsc.baocao.tonghop.vanchuyen + form + tree,form + + + + icsc.baocao.khoiluong.giaonhan.tree + icsc.baocao.khoiluong.giaonhan + + + + + + + + + + + + 2.52 TH khối lượng GN-VC + icsc.baocao.khoiluong.giaonhan + form + tree,form + + + + icsc.baocao.khoiluong.tiencuoc.giaonhan.tree + icsc.baocao.khoiluong.tiencuoc.giaonhan + + + + + + + + + + + 2.53 TH khối lượng tiền cước vc + icsc.baocao.khoiluong.tiencuoc.giaonhan + form + tree,form + + + + icsc.baocao.denghithanhtoan.tree + icsc.baocao.denghithanhtoan + + + + + + + + + + + + 2.6 TK đề nghị thanh toán + icsc.baocao.denghithanhtoan + form + tree,form + + + + icsc.thanhtoan.vanchuyen.tree + icsc.thanhtoan.vanchuyen + + + + + + + + + + + icsc.thanhtoan.vanchuyen.form + icsc.thanhtoan.vanchuyen + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + 2.702 Thanh toán cước VC thuê ngoài + icsc.thanhtoan.vanchuyen + form + tree,form + + + + + icsc.tongtien.thanhtoan.vanchuyen.tree + icsc.tongtien.thanhtoan.vanchuyen + + + + + + + + + + 2.701 Tổng tiền cước phải trả + icsc.tongtien.thanhtoan.vanchuyen + form + tree,form + + + + icsc.baocao.guihang.duongsat.tree + icsc.baocao.guihang.duongsat + + + + + + + + + + + + 2.8 BK gửi hàng đường sắt + icsc.baocao.guihang.duongsat + form + tree,form + + + + icsc.baocao.uyquyen.gioithieu.tree + icsc.baocao.uyquyen.gioithieu + + + + + + + + + + 2.9 Giấy ủy quyền vận chuyển + icsc.baocao.uyquyen.gioithieu + form + tree,form + + + + + icsc.baocao.tonghop.kho.tree + icsc.baocao.tonghop.kho + + + + + + + + + + + + + + 3.1 BC Nhập xuất tồn kho + icsc.baocao.tonghop.kho + form + tree,form + + + + icsc.baocao.tonghop.phatsinh.tree + icsc.baocao.tonghop.phatsinh + + + + + + + + + + + 3.2 BC Tổng hợp phát sinh + icsc.baocao.tonghop.phatsinh + form + tree,form + + + + icsc.baocao.xuatkho.tree + icsc.baocao.xuatkho + + + + + + + + + 3.3 Danh sách mã quản lý KH + icsc.baocao.xuatkho + form + tree,form + + + + icsc.baocao.xuatkho2.tree + icsc.baocao.xuatkho2 + + + + + + + + + + 3.4 Báo cáo phiếu xuất kho + icsc.baocao.xuatkho2 + form + tree,form + + + + icsc.baocao.bangke.phieu.tree + icsc.baocao.bangke.phieu + + + + + + + + + + + 3.5 Bảng kê PXK, HĐ, PVC + icsc.baocao.bangke.phieu + form + tree,form + + + + icsc.baocao.bienban.kiemke.tree + icsc.baocao.bienban.kiemke + + + + + + + + + + + + 3.6 Biên bản kiểm kê + icsc.baocao.bienban.kiemke + form + tree,form + + + + icsc.baocao.phieuthu.tienthu.tree + icsc.baocao.phieuthu.tienthu + + + + + + + + + + + Báo cáo phiếu thu + icsc.baocao.phieuthu.tienthu + form + tree,form + + + + + + icsc.baocao.thongke.khioiluong.tree + icsc.baocao.thongke.khioiluong + + + + + + + + + + + + + + + + + 2.101 TH vận chuyển thủy-bộ + icsc.baocao.thongke.khioiluong + form + tree,form + + + + + + icsc.baocao.kl.kvkn.tree + icsc.baocao.kl.kvkn + + + + + + + + + + + + + + + + 2.11 KV kho nhập + TKS + icsc.baocao.kl.kvkn + form + tree,form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    \ No newline at end of file diff --git a/addons/knowledge/images/1_config_knowledge.jpeg b/addons/knowledge/images/1_config_knowledge.jpeg new file mode 100644 index 0000000000000..85d897768de9e Binary files /dev/null and b/addons/knowledge/images/1_config_knowledge.jpeg differ diff --git a/addons/knowledge/images/knowledge-hover.png b/addons/knowledge/images/knowledge-hover.png new file mode 100644 index 0000000000000..78f99d8f8df70 Binary files /dev/null and b/addons/knowledge/images/knowledge-hover.png differ diff --git a/addons/knowledge/images/knowledge.png b/addons/knowledge/images/knowledge.png new file mode 100644 index 0000000000000..35c8d5706fdb3 Binary files /dev/null and b/addons/knowledge/images/knowledge.png differ diff --git a/addons/knowledge/res_config.py b/addons/knowledge/res_config.py index fddbb5ebdc38b..4fc5ed413b676 100644 --- a/addons/knowledge/res_config.py +++ b/addons/knowledge/res_config.py @@ -1,42 +1,29 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (C) 2004-2012 OpenERP S.A. (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv - -class knowledge_config_settings(osv.osv_memory): - _name = 'knowledge.config.settings' - _inherit = 'res.config.settings' - _columns = { - 'module_document_page': fields.boolean('Create static web pages', - help="""This installs the module document_page."""), - 'module_document': fields.boolean('Manage documents', - help="""This is a complete document management system, with: user authentication, - full document search (but pptx and docx are not supported), and a document dashboard. - This installs the module document."""), - 'module_document_ftp': fields.boolean('Share repositories (FTP)', - help="""Access your documents in OpenERP through an FTP interface. - This installs the module document_ftp."""), - 'module_document_webdav': fields.boolean('Share repositories (WebDAV)', - help="""Access your documents in OpenERP through WebDAV. - This installs the module document_webdav."""), - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv + +class knowledge_config_settings(osv.osv_memory): + _name = 'knowledge.config.settings' + _inherit = 'res.config.settings' + _columns = {'module_document_page': fields.boolean('Create static web pages', help='This installs the module document_page.'), + 'module_document': fields.boolean('Manage documents', help='This is a complete document management system, with: user authentication,\n full document search (but pptx and docx are not supported), and a document dashboard.\n This installs the module document.'), + 'module_document_ftp': fields.boolean('Share repositories (FTP)', help='Access your documents in OpenERP through an FTP interface.\n This installs the module document_ftp.'), + 'module_document_webdav': fields.boolean('Share repositories (WebDAV)', help='Access your documents in OpenERP through WebDAV.\n This installs the module document_webdav.')} \ No newline at end of file diff --git a/addons/l10n_ar/__init__.pyc_dis b/addons/l10n_ar/__init__.pyc_dis new file mode 100644 index 0000000000000..8dd08eb55f5db --- /dev/null +++ b/addons/l10n_ar/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_ar/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_ar/images/config_chart_l10n_ar.jpeg b/addons/l10n_ar/images/config_chart_l10n_ar.jpeg new file mode 100644 index 0000000000000..f01f18140835c Binary files /dev/null and b/addons/l10n_ar/images/config_chart_l10n_ar.jpeg differ diff --git a/addons/l10n_ar/images/l10n_ar_chart.jpeg b/addons/l10n_ar/images/l10n_ar_chart.jpeg new file mode 100644 index 0000000000000..4af3605fe8a2c Binary files /dev/null and b/addons/l10n_ar/images/l10n_ar_chart.jpeg differ diff --git a/addons/l10n_ar/l10n_ar_chart.xml b/addons/l10n_ar/l10n_ar_chart.xml index bb427a1e1d4b3..062697d5aa651 100644 --- a/addons/l10n_ar/l10n_ar_chart.xml +++ b/addons/l10n_ar/l10n_ar_chart.xml @@ -254,13 +254,6 @@
    - - Argentina - Plan de Cuentas - account.chart.template - default - - - diff --git a/addons/l10n_at/__init__.pyc_dis b/addons/l10n_at/__init__.pyc_dis new file mode 100644 index 0000000000000..bade719e149fe --- /dev/null +++ b/addons/l10n_at/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_at/__init__.py +import account_wizard \ No newline at end of file diff --git a/addons/l10n_at/account_chart.xml b/addons/l10n_at/account_chart.xml index 3d17cd20c86bf..51d0c9a85a882 100644 --- a/addons/l10n_at/account_chart.xml +++ b/addons/l10n_at/account_chart.xml @@ -2519,13 +2519,6 @@
    - - Austria - Chart of Accounts - account.chart.template - default - - - diff --git a/addons/l10n_be/__init__.pyc_dis b/addons/l10n_be/__init__.pyc_dis new file mode 100644 index 0000000000000..87711a63f67ec --- /dev/null +++ b/addons/l10n_be/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_be/__init__.py +import wizard \ No newline at end of file diff --git a/addons/l10n_be/__openerp__.py b/addons/l10n_be/__openerp__.py index 70b5ef96224dd..18943a2bee3ed 100644 --- a/addons/l10n_be/__openerp__.py +++ b/addons/l10n_be/__openerp__.py @@ -65,7 +65,6 @@ 'account_pcmn_belgium.xml', 'account_tax_code_template.xml', 'account_chart_template.xml', - 'account_chart_template.yml', 'account_tax_template.xml', 'wizard/l10n_be_account_vat_declaration_view.xml', 'wizard/l10n_be_vat_intra_view.xml', diff --git a/addons/l10n_be/account_chart_template.xml b/addons/l10n_be/account_chart_template.xml index 6c9f205471c63..55726fdc335c0 100644 --- a/addons/l10n_be/account_chart_template.xml +++ b/addons/l10n_be/account_chart_template.xml @@ -12,13 +12,7 @@ -
    - - Belgian PCMN - account.chart.template - default - - + diff --git a/addons/l10n_be/account_financial_report.xml b/addons/l10n_be/account_financial_report.xml index 2abfce472f7be..ccc118d30aefc 100644 --- a/addons/l10n_be/account_financial_report.xml +++ b/addons/l10n_be/account_financial_report.xml @@ -640,7 +640,7 @@
    - Bénéfice (Perte) de l'exercice avant impôts + Bénéfice (Perte) de l'excercice avant impôts no_detail @@ -674,7 +674,7 @@ - Bénéfice (Perte) de l'exercice + Bénéfice (Perte) de l'excercice no_detail diff --git a/addons/l10n_be/account_pcmn_belgium.xml b/addons/l10n_be/account_pcmn_belgium.xml index 32da80bb1fe18..0d7c275e621df 100644 --- a/addons/l10n_be/account_pcmn_belgium.xml +++ b/addons/l10n_be/account_pcmn_belgium.xml @@ -3628,7 +3628,6 @@ other - Acomptes reçus @@ -3636,7 +3635,6 @@ other - Compensations fournisseurs @@ -3644,7 +3642,6 @@ other - DETTES FISCALES, SALARIALES ET SOCIALES @@ -6341,7 +6338,7 @@ view - + Reprises d'amortissements et de réductions de valeur diff --git a/addons/l10n_be/account_tax_template.xml b/addons/l10n_be/account_tax_template.xml index 6958673ae8b94..7dfd37b2f402f 100644 --- a/addons/l10n_be/account_tax_template.xml +++ b/addons/l10n_be/account_tax_template.xml @@ -2052,8 +2052,6 @@ VAT-IN-V82-CAR-EXC-C1 Frais de voiture - VAT 50% Non Deductible - - 0.105 percent diff --git a/addons/l10n_be/images/1_config_chart_l10n_be.jpeg b/addons/l10n_be/images/1_config_chart_l10n_be.jpeg new file mode 100644 index 0000000000000..a8d13780daec3 Binary files /dev/null and b/addons/l10n_be/images/1_config_chart_l10n_be.jpeg differ diff --git a/addons/l10n_be/images/2_l10n_be_chart.jpeg b/addons/l10n_be/images/2_l10n_be_chart.jpeg new file mode 100644 index 0000000000000..43832e83561ac Binary files /dev/null and b/addons/l10n_be/images/2_l10n_be_chart.jpeg differ diff --git a/addons/l10n_be/l10n_be_sequence.xml b/addons/l10n_be/l10n_be_sequence.xml index 6210d362774e3..1c11d5b48af65 100644 --- a/addons/l10n_be/l10n_be_sequence.xml +++ b/addons/l10n_be/l10n_be_sequence.xml @@ -14,7 +14,6 @@ Declarantnum declarantnum 5 - diff --git a/addons/l10n_be/wizard/__init__.pyc_dis b/addons/l10n_be/wizard/__init__.pyc_dis new file mode 100644 index 0000000000000..74032da9108ea --- /dev/null +++ b/addons/l10n_be/wizard/__init__.pyc_dis @@ -0,0 +1,4 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_be/wizard/__init__.py +import l10n_be_partner_vat_listing +import l10n_be_vat_intra +import l10n_be_account_vat_declaration \ No newline at end of file diff --git a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.pyc_dis b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.pyc_dis new file mode 100644 index 0000000000000..6c4d89a9a4cd4 --- /dev/null +++ b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.pyc_dis @@ -0,0 +1,169 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py +import base64 +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class l10n_be_vat_declaration(osv.osv_memory): + """ Vat Declaration """ + _name = 'l1on_be.vat.declaration' + _description = 'Vat Declaration' + + def _get_xml_data(self, cr, uid, context = None): + if context.get('file_save', False): + return base64.encodestring(context['file_save'].encode('utf8')) + return '' + + _columns = {'name': fields.char('File Name', size=32), + 'period_id': fields.many2one('account.period', 'Period', required=True), + 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', domain=[('parent_id', '=', False)], required=True), + 'msg': fields.text('File created', size=64, readonly=True), + 'file_save': fields.binary('Save File'), + 'ask_restitution': fields.boolean('Ask Restitution', help='It indicates whether a restitution is to make or not?'), + 'ask_payment': fields.boolean('Ask Payment', help='It indicates whether a payment is to make or not?'), + 'client_nihil': fields.boolean('Last Declaration, no clients in client listing', help='Tick this case only if it concerns only the last statement on the civil or cessation of activity: no clients to be included in the client listing.'), + 'comments': fields.text('Comments')} + + def _get_tax_code(self, cr, uid, context = None): + obj_tax_code = self.pool.get('account.tax.code') + obj_user = self.pool.get('res.users') + company_id = obj_user.browse(cr, uid, uid, context=context).company_id.id + tax_code_ids = obj_tax_code.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', False)], context=context) + return tax_code_ids and tax_code_ids[0] or False + + _defaults = {'msg': 'Save the File with .xml extension.', + 'file_save': _get_xml_data, + 'name': 'vat_declaration.xml', + 'tax_code_id': _get_tax_code} + + def create_xml(self, cr, uid, ids, context = None): + obj_tax_code = self.pool.get('account.tax.code') + obj_acc_period = self.pool.get('account.period') + obj_user = self.pool.get('res.users') + obj_partner = self.pool.get('res.partner') + mod_obj = self.pool.get('ir.model.data') + if context is None: + context = {} + list_of_tags = ['00', + '01', + '02', + '03', + '44', + '45', + '46', + '47', + '48', + '49', + '54', + '55', + '56', + '57', + '59', + '61', + '62', + '63', + '64', + '71', + '72', + '81', + '82', + '83', + '84', + '85', + '86', + '87', + '88', + '91'] + data_tax = self.browse(cr, uid, ids[0]) + if data_tax.tax_code_id: + obj_company = data_tax.tax_code_id.company_id + else: + obj_company = obj_user.browse(cr, uid, uid, context=context).company_id + vat_no = obj_company.partner_id.vat + if not vat_no: + raise osv.except_osv(_('Insufficient Data!'), _('No VAT number associated with your company.')) + vat_no = vat_no.replace(' ', '').upper() + vat = vat_no[2:] + tax_code_ids = obj_tax_code.search(cr, uid, [('parent_id', 'child_of', data_tax.tax_code_id.id), ('company_id', '=', obj_company.id)], context=context) + ctx = context.copy() + data = self.read(cr, uid, ids)[0] + ctx['period_id'] = data['period_id'][0] + tax_info = obj_tax_code.read(cr, uid, tax_code_ids, ['code', 'sum_period'], context=ctx) + default_address = obj_partner.address_get(cr, uid, [obj_company.partner_id.id]) + default_address_id = default_address.get('default', obj_company.partner_id.id) + address_id = obj_partner.browse(cr, uid, default_address_id, context) + account_period = obj_acc_period.browse(cr, uid, data['period_id'][0], context=context) + issued_by = vat_no[:2] + comments = data['comments'] or '' + send_ref = str(obj_company.partner_id.id) + str(account_period.date_start[5:7]) + str(account_period.date_stop[:4]) + starting_month = account_period.date_start[5:7] + ending_month = account_period.date_stop[5:7] + quarter = str((int(starting_month) - 1) / 3 + 1) + if not address_id.email: + raise osv.except_osv(_('Insufficient Data!'), _('No email address associated with the company.')) + if not address_id.phone: + raise osv.except_osv(_('Insufficient Data!'), _('No phone associated with the company.')) + file_data = {'issued_by': issued_by, + 'vat_no': vat_no, + 'only_vat': vat_no[2:], + 'cmpny_name': obj_company.name, + 'address': '%s %s' % (address_id.street or '', address_id.street2 or ''), + 'post_code': address_id.zip or '', + 'city': address_id.city or '', + 'country_code': address_id.country_id and address_id.country_id.code or '', + 'email': address_id.email or '', + 'phone': address_id.phone.replace('.', '').replace('/', '').replace('(', '').replace(')', '').replace(' ', ''), + 'send_ref': send_ref, + 'quarter': quarter, + 'month': starting_month, + 'year': str(account_period.date_stop[:4]), + 'client_nihil': data['client_nihil'] and 'YES' or 'NO', + 'ask_restitution': data['ask_restitution'] and 'YES' or 'NO', + 'ask_payment': data['ask_payment'] and 'YES' or 'NO', + 'comments': comments} + data_of_file = '\n\n \n %(only_vat)s\n %(cmpny_name)s\n %(address)s\n %(post_code)s\n %(city)s\n %(country_code)s\n %(email)s\n %(phone)s\n \n \n \n %(only_vat)s\n %(cmpny_name)s\n %(address)s\n %(post_code)s\n %(city)s\n %(country_code)s\n %(email)s\n %(phone)s\n \n \n ' % file_data + if starting_month != ending_month: + data_of_file += '\t\t%(quarter)s\n\t\t' % file_data + else: + data_of_file += '\t\t%(month)s\n\t\t' % file_data + data_of_file += '\t%(year)s' % file_data + data_of_file += '\n\t\t\n' + data_of_file += '\t\t\t' + cases_list = [] + for item in tax_info: + if item['code'] == '91' and ending_month != 12: + continue + if item['code'] and item['sum_period']: + if item['code'] == 'VI': + if item['sum_period'] >= 0: + item['code'] = '71' + else: + item['code'] = '72' + if item['code'] in list_of_tags: + cases_list.append(item) + + cases_list.sort() + for item in cases_list: + grid_amount_data = {'code': str(int(item['code'])), + 'amount': str(abs(item['sum_period']))} + data_of_file += '\n\t\t\t%(amount)s' % grid_amount_data + + data_of_file += '\n\t\t' + data_of_file += '\n\t\t%(client_nihil)s' % file_data + data_of_file += '\n\t\t' % file_data + data_of_file += '\n\t\t%(comments)s' % file_data + data_of_file += '\n\t \n' + model_data_ids = mod_obj.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_vat_save')], context=context) + resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] + context['file_save'] = data_of_file + return {'name': _('Save XML For Vat declaration'), + 'context': context, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'l1on_be.vat.declaration', + 'views': [(resource_id, 'form')], + 'view_id': 'view_vat_save', + 'type': 'ir.actions.act_window', + 'target': 'new'} + + +l10n_be_vat_declaration() \ No newline at end of file diff --git a/addons/l10n_be/wizard/l10n_be_partner_vat_listing.pyc_dis b/addons/l10n_be/wizard/l10n_be_partner_vat_listing.pyc_dis new file mode 100644 index 0000000000000..eac3aec35074e --- /dev/null +++ b/addons/l10n_be/wizard/l10n_be_partner_vat_listing.pyc_dis @@ -0,0 +1,242 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py +import time +import base64 +from openerp.tools.translate import _ +from openerp.osv import fields, osv +from openerp.report import report_sxw + +class vat_listing_clients(osv.osv_memory): + _name = 'vat.listing.clients' + _columns = {'name': fields.char('Client Name', size=32), + 'vat': fields.char('VAT', size=64), + 'turnover': fields.float('Base Amount'), + 'vat_amount': fields.float('VAT Amount')} + + +vat_listing_clients() + +class partner_vat(osv.osv_memory): + """ Vat Listing """ + _name = 'partner.vat' + + def get_partner(self, cr, uid, ids, context = None): + obj_period = self.pool.get('account.period') + obj_partner = self.pool.get('res.partner') + obj_vat_lclient = self.pool.get('vat.listing.clients') + obj_model_data = self.pool.get('ir.model.data') + obj_module = self.pool.get('ir.module.module') + data = self.read(cr, uid, ids)[0] + year = data['year'] + date_start = year + '-01-01' + date_stop = year + '-12-31' + if context.get('company_id', False): + company_id = context['company_id'] + else: + company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + period_ids = obj_period.search(cr, uid, [('date_start', '>=', date_start), ('date_stop', '<=', date_stop), ('company_id', '=', company_id)]) + if not period_ids: + raise osv.except_osv(_('Insufficient Data!'), _('No data for the selected year.')) + partners = [] + partner_ids = obj_partner.search(cr, uid, [('vat_subjected', '!=', False), ('vat', 'ilike', 'BE%')], context=context) + if not partner_ids: + raise osv.except_osv(_('Error'), _('No belgium contact with a VAT number in your database.')) + cr.execute("SELECT sub1.partner_id, sub1.name, sub1.vat, sub1.turnover, sub2.vat_amount\n FROM (SELECT l.partner_id, p.name, p.vat, SUM(CASE WHEN c.code ='49' THEN -l.tax_amount ELSE l.tax_amount END) as turnover\n FROM account_move_line l\n LEFT JOIN res_partner p ON l.partner_id = p.id\n LEFT JOIN account_tax_code c ON l.tax_code_id = c.id\n WHERE c.code IN ('00','01','02','03','45','49')\n AND l.partner_id IN %s\n AND l.period_id IN %s\n GROUP BY l.partner_id, p.name, p.vat) AS sub1\n LEFT JOIN (SELECT l2.partner_id, SUM(CASE WHEN c2.code ='64' THEN -l2.tax_amount ELSE l2.tax_amount END) as vat_amount\n FROM account_move_line l2\n LEFT JOIN account_tax_code c2 ON l2.tax_code_id = c2.id\n WHERE c2.code IN ('54','64')\n AND l2.partner_id IN %s\n AND l2.period_id IN %s\n GROUP BY l2.partner_id) AS sub2 ON sub1.partner_id = sub2.partner_id\n ", (tuple(partner_ids), + tuple(period_ids), + tuple(partner_ids), + tuple(period_ids))) + for record in cr.dictfetchall(): + record['vat'] = record['vat'].replace(' ', '').upper() + if record['turnover'] >= data['limit_amount']: + id_client = obj_vat_lclient.create(cr, uid, record, context=context) + partners.append(id_client) + + if not partners: + raise osv.except_osv(_('Insufficient Data!'), _('No data found for the selected year.')) + context.update({'partner_ids': partners, + 'year': data['year'], + 'limit_amount': data['limit_amount']}) + model_data_ids = obj_model_data.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_vat_listing')]) + resource_id = obj_model_data.read(cr, uid, model_data_ids, fields=['res_id'])[0]['res_id'] + return {'name': _('Vat Listing'), + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'partner.vat.list', + 'views': [(resource_id, 'form')], + 'context': context, + 'type': 'ir.actions.act_window', + 'target': 'new'} + + _columns = {'year': fields.char('Year', size=4, required=True), + 'limit_amount': fields.integer('Limit Amount', required=True)} + _defaults = {'year': lambda *a: str(int(time.strftime('%Y')) - 1), + 'limit_amount': 250} + + +partner_vat() + +class partner_vat_list(osv.osv_memory): + """ Partner Vat Listing """ + _name = 'partner.vat.list' + _columns = {'partner_ids': fields.many2many('vat.listing.clients', 'vat_partner_rel', 'vat_id', 'partner_id', 'Clients', help='You can remove clients/partners which you do not want to show in xml file'), + 'name': fields.char('File Name', size=32), + 'file_save': fields.binary('Save File', readonly=True), + 'comments': fields.text('Comments')} + + def _get_partners(self, cr, uid, context = None): + return context.get('partner_ids', []) + + _defaults = {'partner_ids': _get_partners} + + def _get_datas(self, cr, uid, ids, context = None): + obj_vat_lclient = self.pool.get('vat.listing.clients') + datas = [] + data = self.read(cr, uid, ids)[0] + for partner in data['partner_ids']: + if isinstance(partner, list) and partner: + datas.append(partner[2]) + else: + client_data = obj_vat_lclient.read(cr, uid, partner, context=context) + datas.append(client_data) + + client_datas = [] + seq = 0 + sum_tax = 0.0 + sum_turnover = 0.0 + amount_data = {} + for line in datas: + if not line: + continue + seq += 1 + sum_tax += line['vat_amount'] + sum_turnover += line['turnover'] + vat = line['vat'].replace(' ', '').upper() + amount_data = {'seq': str(seq), + 'vat': vat, + 'only_vat': vat[2:], + 'turnover': '%.2f' % line['turnover'], + 'vat_amount': '%.2f' % line['vat_amount'], + 'sum_tax': '%.2f' % sum_tax, + 'sum_turnover': '%.2f' % sum_turnover, + 'partner_name': line['name']} + client_datas += [amount_data] + + return client_datas + + def create_xml(self, cr, uid, ids, context = None): + obj_sequence = self.pool.get('ir.sequence') + obj_users = self.pool.get('res.users') + obj_partner = self.pool.get('res.partner') + obj_model_data = self.pool.get('ir.model.data') + seq_declarantnum = obj_sequence.get(cr, uid, 'declarantnum') + obj_cmpny = obj_users.browse(cr, uid, uid, context=context).company_id + company_vat = obj_cmpny.partner_id.vat + if not company_vat: + raise osv.except_osv(_('Insufficient Data!'), _('No VAT number associated with the company.')) + company_vat = company_vat.replace(' ', '').upper() + SenderId = company_vat[2:] + issued_by = company_vat[:2] + seq_declarantnum = obj_sequence.get(cr, uid, 'declarantnum') + dnum = company_vat[2:] + seq_declarantnum[-4:] + street = city = country = '' + addr = obj_partner.address_get(cr, uid, [obj_cmpny.partner_id.id], ['invoice']) + if addr.get('invoice', False): + ads = obj_partner.browse(cr, uid, [addr['invoice']], context=context)[0] + phone = ads.phone and ads.phone.replace(' ', '') or '' + email = ads.email or '' + name = ads.name or '' + city = ads.city or '' + zip = obj_partner.browse(cr, uid, ads.id, context=context).zip or '' + if not city: + city = '' + if ads.street: + street = ads.street + if ads.street2: + street += ' ' + ads.street2 + if ads.country_id: + country = ads.country_id.code + data = self.read(cr, uid, ids)[0] + sender_date = time.strftime('%Y-%m-%d') + comp_name = obj_cmpny.name + if not email: + raise osv.except_osv(_('Insufficient Data!'), _('No email address associated with the company.')) + if not phone: + raise osv.except_osv(_('Insufficient Data!'), _('No phone associated with the company.')) + annual_listing_data = {'issued_by': issued_by, + 'company_vat': company_vat, + 'comp_name': comp_name, + 'street': street, + 'zip': zip, + 'city': city, + 'country': country, + 'email': email, + 'phone': phone, + 'SenderId': SenderId, + 'period': context['year'], + 'comments': data['comments'] or ''} + data_file = '\n\n \n %(SenderId)s\n %(comp_name)s\n %(street)s\n %(zip)s\n %(city)s' + if annual_listing_data['country']: + data_file += '\n\t\t%(country)s' + data_file += '\n %(email)s\n %(phone)s\n ' + data_file = data_file % annual_listing_data + data_comp = '\n \n %(SenderId)s\n %(comp_name)s\n %(street)s\n %(zip)s\n %(city)s\n %(country)s\n %(email)s\n %(phone)s\n \n %(period)s\n ' % annual_listing_data + client_datas = self._get_datas(cr, uid, ids, context=context) + if not client_datas: + raise osv.except_osv(_('Data Insufficient!'), _('No data available for the client.')) + data_client_info = '' + for amount_data in client_datas: + data_client_info += '\n \n %(only_vat)s\n %(turnover)s\n %(vat_amount)s\n ' % amount_data + + amount_data_begin = client_datas[-1] + amount_data_begin.update({'dnum': dnum}) + data_begin = '\n \n' % amount_data_begin + data_end = '\n\n %(comments)s\n \n\n' % annual_listing_data + data_file += data_begin + data_comp + data_client_info + data_end + file_save = base64.encodestring(data_file.encode('utf8')) + self.write(cr, uid, ids, {'file_save': file_save, + 'name': 'vat_list.xml'}, context=context) + model_data_ids = obj_model_data.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_vat_listing_result')]) + resource_id = obj_model_data.read(cr, uid, model_data_ids, fields=['res_id'])[0]['res_id'] + return {'name': _('XML File has been Created'), + 'res_id': ids[0], + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'partner.vat.list', + 'views': [(resource_id, 'form')], + 'context': context, + 'type': 'ir.actions.act_window', + 'target': 'new'} + + def print_vatlist(self, cr, uid, ids, context = None): + if context is None: + context = {} + obj_vat_lclient = self.pool.get('vat.listing.clients') + datas = {'ids': []} + datas['model'] = 'res.company' + datas['year'] = context['year'] + datas['limit_amount'] = context['limit_amount'] + datas['client_datas'] = self._get_datas(cr, uid, ids, context=context) + if not datas['client_datas']: + raise osv.except_osv(_('Error!'), _('No record to print.')) + return {'type': 'ir.actions.report.xml', + 'report_name': 'partner.vat.listing.print', + 'datas': datas} + + +partner_vat_list() + +class partner_vat_listing_print(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(partner_vat_listing_print, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time}) + + def set_context(self, objects, data, ids, report_type = None): + client_datas = data['client_datas'] + self.localcontext.update({'year': data['year'], + 'sum_turnover': client_datas[-1]['sum_turnover'], + 'sum_tax': client_datas[-1]['sum_tax'], + 'client_list': client_datas}) + super(partner_vat_listing_print, self).set_context(objects, data, ids) + + +report_sxw.report_sxw('report.partner.vat.listing.print', 'res.partner', 'addons/l10n_be/wizard/l10n_be_partner_vat_listing.rml', parser=partner_vat_listing_print, header=False) \ No newline at end of file diff --git a/addons/l10n_be/wizard/l10n_be_vat_intra.pyc_dis b/addons/l10n_be/wizard/l10n_be_vat_intra.pyc_dis new file mode 100644 index 0000000000000..afb1c2f9df96b --- /dev/null +++ b/addons/l10n_be/wizard/l10n_be_vat_intra.pyc_dis @@ -0,0 +1,227 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_be/wizard/l10n_be_vat_intra.py +import time +import base64 +from openerp.osv import fields, osv +from openerp.tools.translate import _ +from openerp.report import report_sxw + +class partner_vat_intra(osv.osv_memory): + """ + Partner Vat Intra + """ + _name = 'partner.vat.intra' + _description = 'Partner VAT Intra' + + def _get_xml_data(self, cr, uid, context = None): + if context.get('file_save', False): + return base64.encodestring(context['file_save'].encode('utf8')) + return '' + + def _get_europe_country(self, cursor, user, context = None): + return self.pool.get('res.country').search(cursor, user, [('code', 'in', ['AT', + 'BG', + 'CY', + 'CZ', + 'DK', + 'EE', + 'FI', + 'FR', + 'DE', + 'GR', + 'HU', + 'IE', + 'IT', + 'LV', + 'LT', + 'LU', + 'MT', + 'NL', + 'PL', + 'PT', + 'RO', + 'SK', + 'SI', + 'ES', + 'SE', + 'GB'])]) + + _columns = {'name': fields.char('File Name', size=32), + 'period_code': fields.char('Period Code', size=6, required=True, help="This is where you have to set the period code for the intracom declaration using the format: ppyyyy\n PP can stand for a month: from '01' to '12'.\n PP can stand for a trimester: '31','32','33','34'\n The first figure means that it is a trimester,\n The second figure identify the trimester.\n PP can stand for a complete fiscal year: '00'.\n YYYY stands for the year (4 positions).\n "), + 'period_ids': fields.many2many('account.period', 'account_period_rel', 'acc_id', 'period_id', 'Period (s)', help='Select here the period(s) you want to include in your intracom declaration'), + 'tax_code_id': fields.many2one('account.tax.code', 'Company', domain=[('parent_id', '=', False)], help="Keep empty to use the user's company", required=True), + 'test_xml': fields.boolean('Test XML file', help='Sets the XML output as test file'), + 'mand_id': fields.char('Reference', size=14, help='Reference given by the Representative of the sending company.'), + 'msg': fields.text('File created', size=14, readonly=True), + 'no_vat': fields.text('Partner With No VAT', size=14, readonly=True, help='The Partner whose VAT number is not defined and they are not included in XML File.'), + 'file_save': fields.binary('Save File', readonly=True), + 'country_ids': fields.many2many('res.country', 'vat_country_rel', 'vat_id', 'country_id', 'European Countries'), + 'comments': fields.text('Comments')} + + def _get_tax_code(self, cr, uid, context = None): + obj_tax_code = self.pool.get('account.tax.code') + obj_user = self.pool.get('res.users') + company_id = obj_user.browse(cr, uid, uid, context=context).company_id.id + tax_code_ids = obj_tax_code.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', False)], context=context) + return tax_code_ids and tax_code_ids[0] or False + + _defaults = {'country_ids': _get_europe_country, + 'file_save': _get_xml_data, + 'name': 'vat_intra.xml', + 'tax_code_id': _get_tax_code} + + def _get_datas(self, cr, uid, ids, context = None): + """Collects require data for vat intra xml + :param ids: id of wizard. + :return: dict of all data to be used to generate xml for Partner VAT Intra. + :rtype: dict + """ + if context is None: + context = {} + obj_user = self.pool.get('res.users') + obj_sequence = self.pool.get('ir.sequence') + obj_partner = self.pool.get('res.partner') + xmldict = {} + post_code = street = city = country = data_clientinfo = '' + seq = amount_sum = 0 + wiz_data = self.browse(cr, uid, ids[0], context=context) + comments = wiz_data.comments + if wiz_data.tax_code_id: + data_company = wiz_data.tax_code_id.company_id + else: + data_company = obj_user.browse(cr, uid, uid, context=context).company_id + company_vat = data_company.partner_id.vat + if not company_vat: + raise osv.except_osv(_('Insufficient Data!'), _('No VAT number associated with your company.')) + company_vat = company_vat.replace(' ', '').upper() + issued_by = company_vat[:2] + if len(wiz_data.period_code) != 6: + raise osv.except_osv(_('Error!'), _('Period code is not valid.')) + if not wiz_data.period_ids: + raise osv.except_osv(_('Insufficient Data!'), _('Please select at least one Period.')) + p_id_list = obj_partner.search(cr, uid, [('vat', '!=', False)], context=context) + if not p_id_list: + raise osv.except_osv(_('Insufficient Data!'), _('No partner has a VAT number associated with him.')) + seq_declarantnum = obj_sequence.get(cr, uid, 'declarantnum') + dnum = company_vat[2:] + seq_declarantnum[-4:] + addr = obj_partner.address_get(cr, uid, [data_company.partner_id.id], ['invoice']) + email = data_company.partner_id.email or '' + phone = data_company.partner_id.phone or '' + if addr.get('invoice', False): + ads = obj_partner.browse(cr, uid, [addr['invoice']])[0] + city = ads.city or '' + post_code = ads.zip or '' + if ads.street: + street = ads.street + if ads.street2: + street += ' ' + street += ads.street2 + if ads.country_id: + country = ads.country_id.code + if not country: + country = company_vat[:2] + if not email: + raise osv.except_osv(_('Insufficient Data!'), _('No email address associated with the company.')) + if not phone: + raise osv.except_osv(_('Insufficient Data!'), _('No phone associated with the company.')) + xmldict.update({'company_name': data_company.name, + 'company_vat': company_vat, + 'vatnum': company_vat[2:], + 'mand_id': wiz_data.mand_id, + 'sender_date': str(time.strftime('%Y-%m-%d')), + 'street': street, + 'city': city, + 'post_code': post_code, + 'country': country, + 'email': email, + 'phone': phone.replace('/', '').replace('.', '').replace('(', '').replace(')', '').replace(' ', ''), + 'period': wiz_data.period_code, + 'clientlist': [], + 'comments': comments, + 'issued_by': issued_by}) + codes = ('44', '46L', '46T', '48s44', '48s46L', '48s46T') + cr.execute("SELECT p.name As partner_name, l.partner_id AS partner_id, p.vat AS vat,\n (CASE WHEN t.code = '48s44' THEN '44'\n WHEN t.code = '48s46L' THEN '46L'\n WHEN t.code = '48s46T' THEN '46T'\n ELSE t.code END) AS intra_code,\n SUM(CASE WHEN t.code in ('48s44','48s46L','48s46T') THEN -l.tax_amount ELSE l.tax_amount END) AS amount\n FROM account_move_line l\n LEFT JOIN account_tax_code t ON (l.tax_code_id = t.id)\n LEFT JOIN res_partner p ON (l.partner_id = p.id)\n WHERE t.code IN %s\n AND l.period_id IN %s\n AND t.company_id = %s\n GROUP BY p.name, l.partner_id, p.vat, intra_code", (codes, tuple([ p.id for p in wiz_data.period_ids ]), data_company.id)) + p_count = 0 + for row in cr.dictfetchall(): + if not row['vat']: + row['vat'] = '' + p_count += 1 + seq += 1 + amt = row['amount'] or 0.0 + amount_sum += amt + intra_code = row['intra_code'] == '44' and 'S' or row['intra_code'] == '46L' and 'L' or row['intra_code'] == '46T' and 'T' or '' + xmldict['clientlist'].append({'partner_name': row['partner_name'], + 'seq': seq, + 'vatnum': row['vat'][2:].replace(' ', '').upper(), + 'vat': row['vat'], + 'country': row['vat'][:2], + 'amount': round(amt, 2), + 'intra_code': row['intra_code'], + 'code': intra_code}) + + xmldict.update({'dnum': dnum, + 'clientnbr': str(seq), + 'amountsum': round(amount_sum, 2), + 'partner_wo_vat': p_count}) + return xmldict + + def create_xml(self, cursor, user, ids, context = None): + """Creates xml that is to be exported and sent to estate for partner vat intra. + :return: Value for next action. + :rtype: dict + """ + mod_obj = self.pool.get('ir.model.data') + xml_data = self._get_datas(cursor, user, ids, context=context) + month_quarter = xml_data['period'][:2] + year = xml_data['period'][2:] + data_file = '' + data_head = '\n\n \n %(company_vat)s\n %(company_name)s\n %(street)s\n %(post_code)s\n %(city)s\n %(country)s\n %(email)s\n %(phone)s\n ' % xml_data + if xml_data['mand_id']: + data_head += '\n\t\t%(mand_id)s' % xml_data + data_comp_period = '\n\t\t\n\t\t\t%(vatnum)s\n\t\t\t%(company_name)s\n\t\t\t%(street)s\n\t\t\t%(post_code)s\n\t\t\t%(city)s\n\t\t\t%(country)s\n\t\t\t%(email)s\n\t\t\t%(phone)s\n\t\t' % xml_data + if month_quarter.startswith('3'): + data_comp_period += '\n\t\t\n\t\t\t' + month_quarter[1] + ' \n\t\t\t' + year + '\n\t\t' + elif month_quarter.startswith('0') and month_quarter.endswith('0'): + data_comp_period += '\n\t\t\n\t\t\t' + year + '\n\t\t' + else: + data_comp_period += '\n\t\t\n\t\t\t' + month_quarter + ' \n\t\t\t' + year + '\n\t\t' + data_clientinfo = '' + for client in xml_data['clientlist']: + if not client['vatnum']: + raise osv.except_osv(_('Insufficient Data!'), _('No vat number defined for %s.') % client['partner_name']) + data_clientinfo += '\n\t\t\n\t\t\t%(vatnum)s\n\t\t\t%(code)s\n\t\t\t%(amount).2f\n\t\t' % client + + data_decl = '\n\t' % xml_data + data_file += data_head + data_decl + data_comp_period + data_clientinfo + '\n\t\t%(comments)s\n\t\n' % xml_data + context['file_save'] = data_file + model_data_ids = mod_obj.search(cursor, user, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_vat_intra_save')], context=context) + resource_id = mod_obj.read(cursor, user, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] + return {'name': _('Save'), + 'context': context, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'partner.vat.intra', + 'views': [(resource_id, 'form')], + 'view_id': 'view_vat_intra_save', + 'type': 'ir.actions.act_window', + 'target': 'new'} + + def preview(self, cr, uid, ids, context = None): + xml_data = self._get_datas(cr, uid, ids, context=context) + datas = {'ids': [], + 'model': 'partner.vat.intra', + 'form': xml_data} + return {'type': 'ir.actions.report.xml', + 'report_name': 'partner.vat.intra.print', + 'datas': datas} + + +partner_vat_intra() + +class vat_intra_print(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(vat_intra_print, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time}) + + +report_sxw.report_sxw('report.partner.vat.intra.print', 'partner.vat.intra', 'addons/l10n_be/wizard/l10n_be_vat_intra_print.rml', parser=vat_intra_print, header='internal') \ No newline at end of file diff --git a/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml b/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml index a782b98621a73..ff1a2c2064d0e 100644 --- a/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml +++ b/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml @@ -105,7 +105,7 @@ [[ l['vat'] ]] - [[ u'%s (%s)' % (l['code'], l['intra_code']) ]] + [[ l['code'] (l['intra_code']) ]] [[ formatLang(l['amount'], currency_obj=company.currency_id) ]] diff --git a/addons/l10n_be_coda/images/coda_logs.jpeg b/addons/l10n_be_coda/images/coda_logs.jpeg new file mode 100644 index 0000000000000..4e649ab0f7201 Binary files /dev/null and b/addons/l10n_be_coda/images/coda_logs.jpeg differ diff --git a/addons/l10n_be_coda/images/import_coda_logs.jpeg b/addons/l10n_be_coda/images/import_coda_logs.jpeg new file mode 100644 index 0000000000000..012282f75f863 Binary files /dev/null and b/addons/l10n_be_coda/images/import_coda_logs.jpeg differ diff --git a/addons/l10n_be_hr_payroll/__init__.pyc_dis b/addons/l10n_be_hr_payroll/__init__.pyc_dis new file mode 100644 index 0000000000000..c5b7cb939eed2 --- /dev/null +++ b/addons/l10n_be_hr_payroll/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_be_hr_payroll/__init__.py +import l10n_be_hr_payroll \ No newline at end of file diff --git a/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.pyc_dis b/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.pyc_dis new file mode 100644 index 0000000000000..db1cad0895726 --- /dev/null +++ b/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.pyc_dis @@ -0,0 +1,30 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.py +from openerp.osv import fields, osv +import openerp.addons.decimal_precision as dp + +class hr_contract_be(osv.osv): + _inherit = 'hr.contract' + _columns = {'travel_reimbursement_amount': fields.float('Reimbursement of travel expenses', digits_compute=dp.get_precision('Payroll')), + 'car_company_amount': fields.float('Company car employer', digits_compute=dp.get_precision('Payroll')), + 'car_employee_deduction': fields.float('Company Car Deduction for Worker', digits_compute=dp.get_precision('Payroll')), + 'misc_onss_deduction': fields.float('Miscellaneous exempt ONSS ', digits_compute=dp.get_precision('Payroll')), + 'meal_voucher_amount': fields.float('Check Value Meal ', digits_compute=dp.get_precision('Payroll')), + 'meal_voucher_employee_deduction': fields.float('Check Value Meal - by worker ', digits_compute=dp.get_precision('Payroll')), + 'insurance_employee_deduction': fields.float('Insurance Group - by worker ', digits_compute=dp.get_precision('Payroll')), + 'misc_advantage_amount': fields.float('Benefits of various nature ', digits_compute=dp.get_precision('Payroll')), + 'additional_net_amount': fields.float('Net supplements', digits_compute=dp.get_precision('Payroll')), + 'retained_net_amount': fields.float('Net retained ', digits_compute=dp.get_precision('Payroll'))} + + +hr_contract_be() + +class hr_employee_be(osv.osv): + _inherit = 'hr.employee' + _columns = {'spouse_fiscal_status': fields.selection([('without income', 'Without Income'), ('with income', 'With Income')], 'Tax status for spouse'), + 'disabled_spouse_bool': fields.boolean('Disabled Spouse', help='if recipient spouse is declared disabled by law'), + 'disabled_children_bool': fields.boolean('Disabled Children', help='if recipient children is/are declared disabled by law'), + 'resident_bool': fields.boolean('Nonresident', help='if recipient lives in a foreign country'), + 'disabled_children_number': fields.integer('Number of disabled children')} + + +hr_employee_be() \ No newline at end of file diff --git a/addons/l10n_be_hr_payroll_account/__init__.pyc_dis b/addons/l10n_be_hr_payroll_account/__init__.pyc_dis new file mode 100644 index 0000000000000..40b113c78168b --- /dev/null +++ b/addons/l10n_be_hr_payroll_account/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_be_hr_payroll_account/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_bo/__init__.pyc_dis b/addons/l10n_bo/__init__.pyc_dis new file mode 100644 index 0000000000000..036dc168df36a --- /dev/null +++ b/addons/l10n_bo/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_bo/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_bo/l10n_bo_chart.xml b/addons/l10n_bo/l10n_bo_chart.xml index c31020941031a..dc9e9598909d9 100644 --- a/addons/l10n_bo/l10n_bo_chart.xml +++ b/addons/l10n_bo/l10n_bo_chart.xml @@ -251,13 +251,6 @@ - - Bolivia - Plan de Cuentas - account.chart.template - default - - - diff --git a/addons/l10n_br/__init__.pyc_dis b/addons/l10n_br/__init__.pyc_dis new file mode 100644 index 0000000000000..f4717cfa46e2d --- /dev/null +++ b/addons/l10n_br/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_br/__init__.py +import account \ No newline at end of file diff --git a/addons/l10n_br/account.pyc_dis b/addons/l10n_br/account.pyc_dis new file mode 100644 index 0000000000000..fd0e075d1d910 --- /dev/null +++ b/addons/l10n_br/account.pyc_dis @@ -0,0 +1,138 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_br/account.py +from openerp import pooler +from openerp.osv import fields, osv +TAX_CODE_COLUMNS = {'domain': fields.char('Domain', size=32, help='This field is only used if you develop your own module allowing developers to create specific taxes in a custom domain.'), + 'tax_discount': fields.boolean('Discount this Tax in Prince', help='Mark it for (ICMS, PIS, COFINS and others taxes included).')} +TAX_DEFAULTS = {'base_reduction': 0, + 'amount_mva': 0} + +class account_tax_code_template(osv.osv): + """ Add fields used to define some brazilian taxes """ + _inherit = 'account.tax.code.template' + _columns = TAX_CODE_COLUMNS + + def generate_tax_code(self, cr, uid, tax_code_root_id, company_id, context = None): + """This function generates the tax codes from the templates of tax + code that are children of the given one passed in argument. Then it + returns a dictionary with the mappping between the templates and the + real objects. + + :param tax_code_root_id: id of the root of all the tax code templates + to process. + :param company_id: id of the company the wizard is running for + :returns: dictionary with the mappping between the templates and the + real objects. + :rtype: dict + """ + obj_tax_code_template = self.pool.get('account.tax.code.template') + obj_tax_code = self.pool.get('account.tax.code') + tax_code_template_ref = {} + company = self.pool.get('res.company').browse(cr, uid, company_id, context=context) + children_tax_code_template = tax_code_root_id and obj_tax_code_template.search(cr, uid, [('parent_id', 'child_of', [tax_code_root_id])], order='id') or [] + for tax_code_template in obj_tax_code_template.browse(cr, uid, children_tax_code_template, context=context): + parent_id = tax_code_template.parent_id and tax_code_template.parent_id.id in tax_code_template_ref and tax_code_template_ref[tax_code_template.parent_id.id] or False + vals = {'name': tax_code_root_id == tax_code_template.id and company.name or tax_code_template.name, + 'code': tax_code_template.code, + 'info': tax_code_template.info, + 'parent_id': parent_id, + 'company_id': company_id, + 'sign': tax_code_template.sign, + 'domain': tax_code_template.domain, + 'tax_discount': tax_code_template.tax_discount} + rec_list = obj_tax_code.search(cr, uid, [('name', '=', vals['name']), + ('parent_id', '=', parent_id), + ('code', '=', vals['code']), + ('company_id', '=', vals['company_id'])], context=context) + if not rec_list: + new_tax_code = obj_tax_code.create(cr, uid, vals) + tax_code_template_ref[tax_code_template.id] = new_tax_code + + return tax_code_template_ref + + +class account_tax_code(osv.osv): + """ Add fields used to define some brazilian taxes """ + _inherit = 'account.tax.code' + _columns = TAX_CODE_COLUMNS + + +def get_precision_tax(): + + def change_digit_tax(cr): + res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, 1, 'Account') + return (16, res + 2) + + return change_digit_tax + + +class account_tax_template(osv.osv): + """ Add fields used to define some brazilian taxes """ + _inherit = 'account.tax.template' + _columns = {'tax_discount': fields.boolean('Discount this Tax in Prince', help='Mark it for (ICMS, PIS e etc.).'), + 'base_reduction': fields.float('Redution', required=True, digits_compute=get_precision_tax(), help='Um percentual decimal em % entre 0-1.'), + 'amount_mva': fields.float('MVA Percent', required=True, digits_compute=get_precision_tax(), help='Um percentual decimal em % entre 0-1.'), + 'type': fields.selection([('percent', 'Percentage'), + ('fixed', 'Fixed Amount'), + ('none', 'None'), + ('code', 'Python Code'), + ('balance', 'Balance'), + ('quantity', 'Quantity')], 'Tax Type', required=True, help='The computation method for the tax amount.')} + _defaults = TAX_DEFAULTS + + def _generate_tax(self, cr, uid, tax_templates, tax_code_template_ref, company_id, context = None): + """ + This method generate taxes from templates. + + :param tax_templates: list of browse record of the tax templates to process + :param tax_code_template_ref: Taxcode templates reference. + :param company_id: id of the company the wizard is running for + :returns: + { + 'tax_template_to_tax': mapping between tax template and the newly generated taxes corresponding, + 'account_dict': dictionary containing a to-do list with all the accounts to assign on new taxes + } + """ + result = super(account_tax_template, self)._generate_tax(cr, uid, tax_templates, tax_code_template_ref, company_id, context) + tax_templates = self.browse(cr, uid, result['tax_template_to_tax'].keys(), context) + obj_acc_tax = self.pool.get('account.tax') + for tax_template in tax_templates: + if tax_template.tax_code_id: + obj_acc_tax.write(cr, uid, result['tax_template_to_tax'][tax_template.id], {'domain': tax_template.tax_code_id.domain, + 'tax_discount': tax_template.tax_code_id.tax_discount}) + + return result + + def onchange_tax_code_id(self, cr, uid, ids, tax_code_id, context = None): + result = {'value': {}} + if not tax_code_id: + return result + obj_tax_code = self.pool.get('account.tax.code.template').browse(cr, uid, tax_code_id) + if obj_tax_code: + result['value']['tax_discount'] = obj_tax_code.tax_discount + result['value']['domain'] = obj_tax_code.domain + return result + + +class account_tax(osv.osv): + """ Add fields used to define some brazilian taxes """ + _inherit = 'account.tax' + _columns = {'tax_discount': fields.boolean('Discount this Tax in Prince', help='Mark it for (ICMS, PIS e etc.).'), + 'base_reduction': fields.float('Redution', required=True, digits_compute=get_precision_tax(), help='Um percentual decimal em % entre 0-1.'), + 'amount_mva': fields.float('MVA Percent', required=True, digits_compute=get_precision_tax(), help='Um percentual decimal em % entre 0-1.'), + 'type': fields.selection([('percent', 'Percentage'), + ('fixed', 'Fixed Amount'), + ('none', 'None'), + ('code', 'Python Code'), + ('balance', 'Balance'), + ('quantity', 'Quantity')], 'Tax Type', required=True, help='The computation method for the tax amount.')} + _defaults = TAX_DEFAULTS + + def onchange_tax_code_id(self, cr, uid, ids, tax_code_id, context = None): + result = {'value': {}} + if not tax_code_id: + return result + obj_tax_code = self.pool.get('account.tax.code').browse(cr, uid, tax_code_id) + if obj_tax_code: + result['value']['tax_discount'] = obj_tax_code.tax_discount + result['value']['domain'] = obj_tax_code.domain + return result \ No newline at end of file diff --git a/addons/l10n_br/account_view.xml b/addons/l10n_br/account_view.xml index 0dd246eb3e81f..4eaffd3e27aeb 100644 --- a/addons/l10n_br/account_view.xml +++ b/addons/l10n_br/account_view.xml @@ -34,14 +34,14 @@ - + - - - + + + @@ -52,14 +52,14 @@ - + - - - + + + diff --git a/addons/l10n_br/data/account_chart_template.xml b/addons/l10n_br/data/account_chart_template.xml index 5e47f2ed13607..c99bbd03d2f5a 100644 --- a/addons/l10n_br/data/account_chart_template.xml +++ b/addons/l10n_br/data/account_chart_template.xml @@ -12,14 +12,6 @@ - - - Planilha de Contas Brasileira - account.chart.template - default - - - diff --git a/addons/l10n_br/data/account_tax_template.xml b/addons/l10n_br/data/account_tax_template.xml index c3ecf449838b0..1e6efd5c61679 100644 --- a/addons/l10n_br/data/account_tax_template.xml +++ b/addons/l10n_br/data/account_tax_template.xml @@ -242,22 +242,6 @@ - - IPI 24% - IPI Saída 24% - 0.24 - sale - - - - - - - - - - - IPI 25% IPI Saída 25% @@ -402,22 +386,6 @@ - - IPI 300% - IPI Saída 300% - 3.00 - sale - - - - - - - - - - - IPI 330% IPI Saída 330% @@ -674,22 +642,6 @@ - - IPI 24% - IPI Entrada 24% - 0.24 - purchase - - - - - - - - - - - IPI 25% IPI Entrada 25% @@ -834,22 +786,6 @@ - - IPI 300% - IPI Entrada 300% - 3.00 - purchase - - - - - - - - - - - IPI 330% IPI Entrada 330% diff --git a/addons/l10n_br/images/1_config_chart_l10n_br.jpeg b/addons/l10n_br/images/1_config_chart_l10n_br.jpeg new file mode 100644 index 0000000000000..ea5dfe9f5b859 Binary files /dev/null and b/addons/l10n_br/images/1_config_chart_l10n_br.jpeg differ diff --git a/addons/l10n_br/images/2_l10n_br_chart.jpeg b/addons/l10n_br/images/2_l10n_br_chart.jpeg new file mode 100644 index 0000000000000..3419d3e5c68f1 Binary files /dev/null and b/addons/l10n_br/images/2_l10n_br_chart.jpeg differ diff --git a/addons/l10n_ca/__init__.pyc_dis b/addons/l10n_ca/__init__.pyc_dis new file mode 100644 index 0000000000000..0216689b91e6f --- /dev/null +++ b/addons/l10n_ca/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_ca/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_ca/account_chart_en.xml b/addons/l10n_ca/account_chart_en.xml index fe4753e092840..a929e2138428f 100644 --- a/addons/l10n_ca/account_chart_en.xml +++ b/addons/l10n_ca/account_chart_en.xml @@ -339,157 +339,133 @@ LABOUR TAXES TO PAY - - 2141 - - view - - CANADA REVENUE AGENCY - - - - 21411 - - view - - EMPLOYMENT INSURANCE TO PAY - - - - 214111 - - other - - EI - Employees Contribution - - - - 214112 - - other - - EI - Employer Contribution - - - - 21412 - - other - - Federal Income Tax - - - - 21413 - - view - - CANADA PENSION PLAN TO PAY - - - - 214131 - - other - - CPP - Employees Contribution - - - - 214132 - - other - - CPP - Employer Contribution - - - - 2142 - - view - - PROVINCIAL REVENU AGENCY - - - - 21421 - - other - - Health Services Fund to pay - - - - 21422 - - view - - PROVINCIAL PENSION PLAN TO PAY - - - - 214221 - - other - - Provincial Pension Plan - Employees Contribution - - - - 214222 - - other - - Provincial Pension Plan - Employer Contribution - - - - 21423 - - view - - PROVINCIAL PARENTAL INSURANCE PLAN TO PAY - - - - 214231 - - other - - Parental Insurance Plan - Employee Contribution - - - - 214232 - - other - - Parental Insurance Plan - Employer Contribution - - - - 21424 - - other - - Labour Health and Safety to pay - - - - 21425 - - other - - Labour Standards to pay - - - - 21426 - - other - - Provincial Income Tax - + + 2141 + + view + + CANADIAN REVENU AGENCY + + + + 21411 + + view + + EMPLOYMENT INSURANCE TO PAY + + + + 214111 + + other + + EI - Employees Contribution + + + + 214112 + + other + + EI - Employer Contribution + + + + 21412 + + other + + Federal Income Tax + + + + 2142 + + view + + PROVINCIAL REVENU AGENCY + + + + 21421 + + other + + Health Services Fund to pay + + + + 21422 + + view + + ANNUITIES TO PAY + + + + 214221 + + other + + Annuities - Employees Contribution + + + + 214222 + + other + + Annuities - Employer Contribution + + + + 21423 + + view + + PARENTAL INSURANCE PLAN TO PAY + + + + 214231 + + other + + PAP - Employee Contribution + + + + 214232 + + other + + PAP - Employer Contribution + + + + 21424 + + other + + Labour Health and Safety to pay + + + + 21425 + + other + + Labour Standards to pay + + + + 21426 + + other + + Provincial Income Tax + 215 @@ -523,86 +499,6 @@ Stock Received But Not Billed - - 218 - - view - - CURRENT LIABILITIES RELATED TO SALARIES - - - - 2181 - - other - - Salaries to pay - - - - 2183 - - other - - Bonus to pay - - - - 2184 - - other - - Retroactive Payment to pay - - - - 2185 - - view - - GROUP PENSION PLAN TO PAY - - - - 218501 - - other - - Group Pension Plan to pay - Employees Contribution - - - - 218502 - - other - - Group Pension Plan to pay - Employer Contribution - - - - 2186 - - view - - EMPLOYEE BENEFITS - - - - 218601 - - other - - Employee Benefits Provision - Employees Contribution - - - - 218602 - - other - - Employee Benefits Provision - Employer Contribution - - 25 @@ -627,70 +523,14 @@ PROVISIONS FOR PENSIONS AND OTHER POST-EMPLOYMENT ADVANTAGES - - 2521 - - other - - Provision for pension plans - - - - 253 + + 254 view DEFERRED TAXES - - 254 - - view - - NON-CURRENT LIABILITIES RELATED TO SALARIES - - - - 2541 - - view - - Leaves Accruded - - - - 254101 - - other - - Vacations Accruded - - - - 254102 - - other - - Compensatory Days Accruded - - - - 254103 - - other - - Sick Leaves Accruded - - - - 2542 - - other - - Bonus Accruded - - 259 @@ -897,158 +737,93 @@ International Purchases - - 512 - - view - - LABOUR EXPENSES - - - - 5121 - - view - - Salaries - - - - 512101 - - other - - Regular Salaries - - - - 512102 - - other - - Bonus - - - - 512103 - - other - - Retroactive Pay - - - - 5122 - - view - - Leaves Accruded - - - - 512201 - - other - - Vacations Accruded - - - - 512202 - - other - - Compensatory Days Accruded - - - - 512203 - - other - - Sick Leaves Accruded - - - - 5123 - - view - - Employer Contributions - - - - 512301 - - other - - Canada Pension Plan - - - - 512302 - - other - - Employment Insurance - - - - 512303 - - other - - Group Pension Plan - - - - 512304 - - other - - Employee benefits expense - - - - 512310 - - other - - Provincial Pension Plan - - - - 512311 - - other - - Provincial Parental Insurance Plan - - - - 512312 - - other - - Labour Health and Safety - - - - 512313 - - other - - Labour Standards - - - - 512314 - - other - - Health Service Fund - + + 512 + + view + + LABOUR EXPENSES + + + 51201 + + other + + Salaries, wages and commissions + + + + 51202 + + other + + Holidays + + + + 51203 + + other + + Employment Insurance + + + + 51204 + + other + + Health Services Fund + + + + 51205 + + other + + Annuities + + + + 51206 + + other + + Parental Insurance + + + + 51207 + + other + + Labour Health and Safety + + + + 51208 + + other + + Labour Standards + + + + 51209 + + other + + Federal Income Tax + + + + 51210 + + other + + Provincial Income Tax + 513 diff --git a/addons/l10n_ca/account_chart_fr.xml b/addons/l10n_ca/account_chart_fr.xml index 7d358f04bcab3..57e6c84aa716b 100644 --- a/addons/l10n_ca/account_chart_fr.xml +++ b/addons/l10n_ca/account_chart_fr.xml @@ -330,165 +330,141 @@ TVH à payer - 15% - - 214 - - view - - DAS ET CONTRIBUTIONS DE L'EMPLOYEUR À PAYER - - - - 2141 - - view - - AGENCE DU REVENU DU CANADA - - - - 21411 - - view - - ASSURANCE EMPLOI À PAYER - - - - 214111 - - other - - AE - Contribution des employés - - - - 214112 - - other - - AE - Contribution de l'employeur - - - - 21412 - - other - - Impôt fédéral sur les revenus - - - - 21413 - - view - - RÉGIME DE PENSIONS DU CANADA À PAYER - - - - 214131 - - other - - RPC - Contribution des employés - - - - 214132 - - other - - RPC - Contribution de l'employeur - - - - 2142 - - view - - AGENCE PROVINCIALE - - - - 21421 - - other - - Fond des Services de Santé à payer - - - - 21422 - - view - - RÉGIME DE PENSION PROVINCIAL À PAYER - - - - 214221 - - other - - Régime de pension provincial - Contribution des employés - - - - 214222 - - other - - Régime de pension provincial - Contribution de l'employeur - - - - 21423 - - view - - RÉGIME PROVINCIAL D'ASSURANCE PARENTALE À PAYER - - - - 214231 - - other - - Assurance parentale - Contribution des employés - - - - 214232 - - other - - Assurance parentale - Contribution de l'employeur - - - - 21424 - - other - - Santé et Sécurité au Travail à payer - - - - 21425 - - other - - Normes du Travail à payer - - - - 21426 - - other - - Impôt provincial sur les revenus - + + 214 + + view + + IMPÔTS LIÉS AUX SALAIRES À PAYER + + + + 2141 + + view + + AGENCE DU REVENU DU CANADA + + + + 21411 + + view + + ASSURANCE EMPLOI À PAYER + + + + 214111 + + other + + AE - Contribution des employés + + + + 214112 + + other + + AE - Contribution de l'employeur + + + + 21412 + + other + + Impôt fédéral sur les revenus + + + + 2142 + + view + + AGENCE DU REVENU PROVINCIAL + + + + 21421 + + other + + Fond des Services de Santé à payer + + + + 21422 + + view + + RENTES À PAYER + + + + 214221 + + other + + Rentes - Contribution des employés + + + + 214222 + + other + + Rentes - Contribution de l'employeur + + + + 21423 + + view + + ASSURANCE PARENTALE À PAYER + + + + 214231 + + other + + AP - Contribution des employés + + + + 214232 + + other + + AP - Contribution de l'employeur + + + + 21424 + + other + + Santé et Sécurité au Travail à payer + + + + 21425 + + other + + Normes du Travail à payer + + + + 21426 + + other + + Impôt provincial sur les revenus + 215 @@ -522,87 +498,6 @@ Stock reçu non facturé - - 218 - - view - - PASSIFS COURANTS LIÉS AUX SALAIRES - - - - 2181 - - other - - Salaires à payer - - - - 2183 - - other - - Bonis à payer - - - - 2184 - - other - - Paie Rétroactive à payer - - - - - 2185 - - view - - RÉGIMES DE PENSION COLLECTIFS À PAYER - - - - 218501 - - other - - Régimes de pension collectifs à payer - Contribution des employés - - - - 218502 - - other - - Régimes de pension collectifs à payer - Contribution de l'employeur - - - - 2186 - - view - - PROVISION POUR AVANTAGES SOCIAUX - - - - 218601 - - other - - Provision pour avantages sociaux - Contribution de l'employé - - - - 218602 - - other - - Provision pour avantages sociaux - Contribution de l'employeur - - 25 @@ -627,69 +522,21 @@ PROVISIONS POUR RETRAITES ET AUTRES AVANTAGES POSTÉRIEURS À L'EMPLOI - - 253 - - view - - IMPÔTS DIFFÉRÉS - - - - 254 - - view - - PASSIFS NON-COURANTS LIÉS AUX SALAIRES - - - - 2541 - - view - - Congés Accumulés - - - - 254101 - - other - - Vacances Accumulées - - - - 254102 - - other - - Jours Compensatoires Accumulés - - - - 254103 - - other - - Congés de Maladie Accumulés - - - - 2542 - - other - - Bonis Accumulés - - - - 259 - - view - - AUTRES PASSIFS NON-COURANTS - + + 253 + + view + + IMPÔTS DIFFÉRÉS + + + + 254 + + view + + AUTRES PASSIFS NON-COURANTS + @@ -897,157 +744,93 @@ Achats à l'étranger - - 512 - - view - - SALAIRES ET CHARGES SOCIALES - - - - 5121 - - view - - Salaires - - - - 512101 - - other - - Salaires Réguliers - - - - 512102 - - other - - Bonis - - - - 512103 - - other - - Paies Rétroactives - - - - 5122 - - view - - Congés Accumulées - - - - 512201 - - other - - Vacances Accumulées - - - - 512202 - - other - - Jours Compensatoires Accumulées - - - - 512203 - - other - - Congés de Maladie Accumulés - - - - 5123 - - view - - Contributions de l'Employeur - - - - 512301 - - other - - Régime de Pensions du Canada - - - - 512302 - - other - - Assurance Emploi - - - - 512303 - - other - - Régimes de pension collectifs - - - - 512304 - - other - - Dépense d'avantages sociaux - - - - 512310 - - other - - Régime de pension provincial - - - - 512311 - - other - - Régime d'assurance parental provincial - - - - 512312 - - other - - Santé et sécurité au travail - - - - 512313 - - other - - Normes du travail - - - - 512314 - - other - - Fonds des services de santé - + + 512 + + view + + SALAIRES ET CHARGES SOCIALES + + + + 51201 + + other + + Salaires + + + + 51202 + + other + + Vacances + + + + 51203 + + other + + Assurance Emploi + + + + 51204 + + other + + Fonds des services de santé + + + + 51205 + + other + + Rentes + + + + 51206 + + other + + Assurance parentale + + + + 51207 + + other + + Santé et sécurité au travail + + + + 51208 + + other + + Normes du travail + + + + 51209 + + other + + Impôt fédéral + + + + 51210 + + other + + Impôt provincial + 513 diff --git a/addons/l10n_ca/account_chart_template_en.xml b/addons/l10n_ca/account_chart_template_en.xml index ed28df2fb53d6..635fa7b8bb1a6 100644 --- a/addons/l10n_ca/account_chart_template_en.xml +++ b/addons/l10n_ca/account_chart_template_en.xml @@ -1,17 +1,5 @@ - - - - - - - - - - - - @@ -26,12 +14,15 @@ - - Canada - Chart of Accounts for english-speaking provinces - account.chart.template - default - - + + + + + + + + + diff --git a/addons/l10n_ca/account_chart_template_fr.xml b/addons/l10n_ca/account_chart_template_fr.xml index d9d24a06d6900..7d1cc0a9ae2f0 100644 --- a/addons/l10n_ca/account_chart_template_fr.xml +++ b/addons/l10n_ca/account_chart_template_fr.xml @@ -1,17 +1,5 @@ - - - - - - - - - - - - @@ -25,14 +13,16 @@ - - - Canada - Plan comptable pour les provinces francophones - account.chart.template - default - - - + + + + + + + + + + property_stock_valuation_account_id diff --git a/addons/l10n_ca/account_tax_en.xml b/addons/l10n_ca/account_tax_en.xml index 810827b047e52..4c15ebb68ed3b 100644 --- a/addons/l10n_ca/account_tax_en.xml +++ b/addons/l10n_ca/account_tax_en.xml @@ -14,8 +14,6 @@ 1 percent 1 - - @@ -34,8 +32,6 @@ - - @@ -53,8 +49,6 @@ - - @@ -67,8 +61,6 @@ 1 percent - - @@ -87,8 +79,6 @@ - - @@ -106,8 +96,6 @@ - - @@ -120,8 +108,6 @@ 1 percent - - @@ -139,8 +125,6 @@ - - @@ -158,8 +142,6 @@ - - @@ -172,8 +154,6 @@ 1 percent - - @@ -192,8 +172,6 @@ - - @@ -211,8 +189,6 @@ - - @@ -230,8 +206,6 @@ - - @@ -247,8 +221,6 @@ - - @@ -264,8 +236,6 @@ - - @@ -283,8 +253,6 @@ - - @@ -299,8 +267,6 @@ 1 percent 1 - - @@ -319,8 +285,6 @@ - - @@ -338,8 +302,6 @@ - - @@ -352,8 +314,6 @@ 1 percent - - @@ -372,8 +332,6 @@ - - @@ -391,8 +349,6 @@ - - @@ -405,8 +361,6 @@ 1 percent - - @@ -424,8 +378,6 @@ - - @@ -443,8 +395,6 @@ - - @@ -457,8 +407,6 @@ 1 percent - - @@ -477,8 +425,6 @@ - - @@ -496,8 +442,6 @@ - - @@ -515,8 +459,6 @@ - - @@ -532,8 +474,6 @@ - - @@ -549,8 +489,6 @@ - - @@ -568,8 +506,6 @@ - - diff --git a/addons/l10n_ca/account_tax_fr.xml b/addons/l10n_ca/account_tax_fr.xml index 8f01051c0ef00..6c37befa139da 100644 --- a/addons/l10n_ca/account_tax_fr.xml +++ b/addons/l10n_ca/account_tax_fr.xml @@ -14,8 +14,6 @@ 1 1 percent - - @@ -34,8 +32,6 @@ - - @@ -53,8 +49,6 @@ - - @@ -67,8 +61,6 @@ 1 percent - - @@ -87,8 +79,6 @@ - - @@ -106,8 +96,6 @@ - - @@ -120,8 +108,6 @@ 1 percent - - @@ -139,8 +125,6 @@ - - @@ -158,8 +142,6 @@ - - @@ -172,8 +154,6 @@ 1 percent - - @@ -192,8 +172,6 @@ - - @@ -211,8 +189,6 @@ - - @@ -230,8 +206,6 @@ - - @@ -247,8 +221,6 @@ - - @@ -264,8 +236,6 @@ - - @@ -283,8 +253,6 @@ - - @@ -300,8 +268,6 @@ 1 percent 1 - - @@ -320,8 +286,6 @@ - - @@ -339,8 +303,6 @@ - - @@ -353,8 +315,6 @@ 1 percent - - @@ -373,8 +333,6 @@ - - @@ -392,8 +350,6 @@ - - @@ -406,8 +362,6 @@ 1 percent - - @@ -425,8 +379,6 @@ - - @@ -444,8 +396,6 @@ - - @@ -458,8 +408,6 @@ 1 percent - - @@ -478,8 +426,6 @@ - - @@ -497,8 +443,6 @@ - - @@ -516,8 +460,6 @@ - - @@ -533,8 +475,6 @@ - - @@ -550,8 +490,6 @@ - - @@ -569,8 +507,6 @@ - - diff --git a/addons/l10n_ca/images/config_chart_l10n_ca.jpeg b/addons/l10n_ca/images/config_chart_l10n_ca.jpeg new file mode 100644 index 0000000000000..af6eb8ca0fbc5 Binary files /dev/null and b/addons/l10n_ca/images/config_chart_l10n_ca.jpeg differ diff --git a/addons/l10n_ca/images/l10n_ca_chart.jpeg b/addons/l10n_ca/images/l10n_ca_chart.jpeg new file mode 100644 index 0000000000000..da3ccc60964e3 Binary files /dev/null and b/addons/l10n_ca/images/l10n_ca_chart.jpeg differ diff --git a/addons/l10n_ch/__init__.pyc_dis b/addons/l10n_ch/__init__.pyc_dis new file mode 100644 index 0000000000000..4517f534c5aad --- /dev/null +++ b/addons/l10n_ch/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_ch/__init__.py +from . import account_wizard \ No newline at end of file diff --git a/addons/l10n_ch/account_wizard.pyc_dis b/addons/l10n_ch/account_wizard.pyc_dis new file mode 100644 index 0000000000000..9b6100b9252b1 --- /dev/null +++ b/addons/l10n_ch/account_wizard.pyc_dis @@ -0,0 +1,15 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_ch/account_wizard.py +from openerp.osv.orm import TransientModel + +class WizardMultiChartsAccounts(TransientModel): + _inherit = 'wizard.multi.charts.accounts' + + def onchange_chart_template_id(self, cursor, uid, ids, chart_template_id = False, context = None): + if context is None: + context = {} + res = super(WizardMultiChartsAccounts, self).onchange_chart_template_id(cursor, uid, ids, chart_template_id=chart_template_id, context=context) + if chart_template_id: + sterchi_template = self.pool.get('ir.model.data').get_object(cursor, uid, 'l10n_ch', 'l10nch_chart_template') + if sterchi_template.id == chart_template_id: + res['value']['code_digits'] = 0 + return res \ No newline at end of file diff --git a/addons/l10n_ch/images/config_chart_l10n_ch.jpeg b/addons/l10n_ch/images/config_chart_l10n_ch.jpeg new file mode 100644 index 0000000000000..2c47a7d4f9c10 Binary files /dev/null and b/addons/l10n_ch/images/config_chart_l10n_ch.jpeg differ diff --git a/addons/l10n_ch/images/l10n_ch_chart.jpeg b/addons/l10n_ch/images/l10n_ch_chart.jpeg new file mode 100644 index 0000000000000..64261d058e24d Binary files /dev/null and b/addons/l10n_ch/images/l10n_ch_chart.jpeg differ diff --git a/addons/l10n_ch/sterchi_chart/account.xml b/addons/l10n_ch/sterchi_chart/account.xml index ef2d73d59e2cf..ddad62946aeff 100644 --- a/addons/l10n_ch/sterchi_chart/account.xml +++ b/addons/l10n_ch/sterchi_chart/account.xml @@ -177,12 +177,14 @@ Bilan : Debiteurs receivable + unreconciled none Bilan : Fournisseurs payable + unreconciled none @@ -11807,15 +11809,9 @@ + - - Plan comptable STERCHI - account.chart.template - default - - - diff --git a/addons/l10n_ch/sterchi_chart/vat2011.xml b/addons/l10n_ch/sterchi_chart/vat2011.xml index 29e84aa1c1f04..c6732a00ed4c0 100644 --- a/addons/l10n_ch/sterchi_chart/vat2011.xml +++ b/addons/l10n_ch/sterchi_chart/vat2011.xml @@ -148,8 +148,8 @@ - - + + sale @@ -169,8 +169,8 @@ purchase - - + + TVA 2.5% sur invest. et autres ch. (TR) @@ -210,8 +210,8 @@ sale - - + + TVA 3.8% sur achat B&S (TS) @@ -230,8 +230,8 @@ purchase - - + + TVA 3.8% sur invest. et autres ch. (TS) @@ -271,8 +271,8 @@ sale - - + + TVA 8.0% sur achat B&S (TN) @@ -292,8 +292,8 @@ purchase - - + + TVA 8.0% sur invest. et autres ch. (TN) @@ -330,8 +330,8 @@ sale - - + + TVA 0% exclue diff --git a/addons/l10n_cl/__init__.pyc_dis b/addons/l10n_cl/__init__.pyc_dis new file mode 100644 index 0000000000000..3d52d701d58c5 --- /dev/null +++ b/addons/l10n_cl/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_cl/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_cl/images/config_chart_l10n_cl.jpeg b/addons/l10n_cl/images/config_chart_l10n_cl.jpeg new file mode 100644 index 0000000000000..f01f18140835c Binary files /dev/null and b/addons/l10n_cl/images/config_chart_l10n_cl.jpeg differ diff --git a/addons/l10n_cl/images/l10n_cl_chart.jpeg b/addons/l10n_cl/images/l10n_cl_chart.jpeg new file mode 100644 index 0000000000000..4af3605fe8a2c Binary files /dev/null and b/addons/l10n_cl/images/l10n_cl_chart.jpeg differ diff --git a/addons/l10n_cl/l10n_cl_chart.xml b/addons/l10n_cl/l10n_cl_chart.xml index 82d45b1c7019b..8bc4272c2eb0e 100644 --- a/addons/l10n_cl/l10n_cl_chart.xml +++ b/addons/l10n_cl/l10n_cl_chart.xml @@ -249,13 +249,7 @@ - - Chile - Plan de Cuentas - account.chart.template - default - - - + diff --git a/addons/l10n_cn/__init__.pyc_dis b/addons/l10n_cn/__init__.pyc_dis new file mode 100644 index 0000000000000..d3f2f57591ee5 --- /dev/null +++ b/addons/l10n_cn/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_cn/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_cn/account_chart.xml b/addons/l10n_cn/account_chart.xml index 78e3550d1e401..afcb3e04b0c11 100644 --- a/addons/l10n_cn/account_chart.xml +++ b/addons/l10n_cn/account_chart.xml @@ -958,13 +958,6 @@ - - 中国会计科目表 - account.chart.template - default - - - diff --git a/addons/l10n_cn/images/config_chart_l10n_cn.jpeg b/addons/l10n_cn/images/config_chart_l10n_cn.jpeg new file mode 100644 index 0000000000000..4d045a1be9868 Binary files /dev/null and b/addons/l10n_cn/images/config_chart_l10n_cn.jpeg differ diff --git a/addons/l10n_cn/images/l10n_cn_chart.jpeg b/addons/l10n_cn/images/l10n_cn_chart.jpeg new file mode 100644 index 0000000000000..05aa07de61e40 Binary files /dev/null and b/addons/l10n_cn/images/l10n_cn_chart.jpeg differ diff --git a/addons/l10n_co/__init__.pyc_dis b/addons/l10n_co/__init__.pyc_dis new file mode 100644 index 0000000000000..17ee095489d2c --- /dev/null +++ b/addons/l10n_co/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_co/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_co/data/account_chart.xml b/addons/l10n_co/data/account_chart.xml index cead488e30656..2fe4ac9a1b9b8 100644 --- a/addons/l10n_co/data/account_chart.xml +++ b/addons/l10n_co/data/account_chart.xml @@ -103349,13 +103349,6 @@ participacion, de conformidad con las disposiciones legales vigentes. - - Unique Account Chart - PUC - account.chart.template - default - - - diff --git a/addons/l10n_cr/__init__.pyc_dis b/addons/l10n_cr/__init__.pyc_dis new file mode 100644 index 0000000000000..ed7c32b7bc670 --- /dev/null +++ b/addons/l10n_cr/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_cr/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_cr/data/account_chart_template.xml b/addons/l10n_cr/data/account_chart_template.xml index b2ad615a15852..e84ea73fbedc9 100644 --- a/addons/l10n_cr/data/account_chart_template.xml +++ b/addons/l10n_cr/data/account_chart_template.xml @@ -17,13 +17,6 @@ - - Costa Rica - Company 0 - account.chart.template - default - - - Costa Rica - Company 1 @@ -36,12 +29,5 @@ - - Costa Rica - Company 1 - account.chart.template - default - - - diff --git a/addons/l10n_cr/images/config_chart_l10n_cr.jpeg b/addons/l10n_cr/images/config_chart_l10n_cr.jpeg new file mode 100644 index 0000000000000..6ad7eb9b71485 Binary files /dev/null and b/addons/l10n_cr/images/config_chart_l10n_cr.jpeg differ diff --git a/addons/l10n_cr/images/l10n_cr_chart.jpeg b/addons/l10n_cr/images/l10n_cr_chart.jpeg new file mode 100644 index 0000000000000..327d794b9a79b Binary files /dev/null and b/addons/l10n_cr/images/l10n_cr_chart.jpeg differ diff --git a/addons/l10n_de/__init__.pyc_dis b/addons/l10n_de/__init__.pyc_dis new file mode 100644 index 0000000000000..76c3975783cbd --- /dev/null +++ b/addons/l10n_de/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_de/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_de/account_chart_template_skr03.xml b/addons/l10n_de/account_chart_template_skr03.xml index bfd6672afb1b1..058424188f66e 100644 --- a/addons/l10n_de/account_chart_template_skr03.xml +++ b/addons/l10n_de/account_chart_template_skr03.xml @@ -13,12 +13,5 @@ - - Deutscher Kontenplan SKR03 - account.chart.template - default - - - diff --git a/addons/l10n_de/account_chart_template_skr04.xml b/addons/l10n_de/account_chart_template_skr04.xml index 1929a7b07e16d..c33c0d415ebc9 100644 --- a/addons/l10n_de/account_chart_template_skr04.xml +++ b/addons/l10n_de/account_chart_template_skr04.xml @@ -13,12 +13,5 @@ - - Deutscher Kontenplan SKR04 - account.chart.template - default - - - diff --git a/addons/l10n_de/images/config_chart_l10n_de.jpeg b/addons/l10n_de/images/config_chart_l10n_de.jpeg new file mode 100644 index 0000000000000..0c0782a81bfd3 Binary files /dev/null and b/addons/l10n_de/images/config_chart_l10n_de.jpeg differ diff --git a/addons/l10n_de/images/l10n_de_chart.jpeg b/addons/l10n_de/images/l10n_de_chart.jpeg new file mode 100644 index 0000000000000..30cfa09a3000a Binary files /dev/null and b/addons/l10n_de/images/l10n_de_chart.jpeg differ diff --git a/addons/l10n_ec/__init__.pyc_dis b/addons/l10n_ec/__init__.pyc_dis new file mode 100644 index 0000000000000..5ef213b912541 --- /dev/null +++ b/addons/l10n_ec/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_ec/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_ec/account_chart.xml b/addons/l10n_ec/account_chart.xml index 31d85e03dc705..17f1b71d04770 100644 --- a/addons/l10n_ec/account_chart.xml +++ b/addons/l10n_ec/account_chart.xml @@ -4203,13 +4203,6 @@ - - - Ecuador - Chart of Accounts - account.chart.template - default - - diff --git a/addons/l10n_ec/images/config_chart_l10n_ec.jpeg b/addons/l10n_ec/images/config_chart_l10n_ec.jpeg new file mode 100644 index 0000000000000..29307dd008c48 Binary files /dev/null and b/addons/l10n_ec/images/config_chart_l10n_ec.jpeg differ diff --git a/addons/l10n_ec/images/l10n_ec_chart.jpeg b/addons/l10n_ec/images/l10n_ec_chart.jpeg new file mode 100644 index 0000000000000..542361bc02889 Binary files /dev/null and b/addons/l10n_ec/images/l10n_ec_chart.jpeg differ diff --git a/addons/l10n_es/__init__.pyc_dis b/addons/l10n_es/__init__.pyc_dis new file mode 100644 index 0000000000000..432e3bfd0694c --- /dev/null +++ b/addons/l10n_es/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_es/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_es/account_chart.xml b/addons/l10n_es/account_chart.xml index 7e44eb1b613b3..1bc63f6419f32 100644 --- a/addons/l10n_es/account_chart.xml +++ b/addons/l10n_es/account_chart.xml @@ -13334,12 +13334,5 @@ - - Plantilla PGCE completo 2008 - account.chart.template - default - - - diff --git a/addons/l10n_es/account_chart_assoc.xml b/addons/l10n_es/account_chart_assoc.xml index 785d9ed60a53e..670eed53c9b6c 100644 --- a/addons/l10n_es/account_chart_assoc.xml +++ b/addons/l10n_es/account_chart_assoc.xml @@ -12421,13 +12421,6 @@ - - Plantilla PGCE Asociaciones 2008 - account.chart.template - default - - - diff --git a/addons/l10n_es/account_chart_pymes.xml b/addons/l10n_es/account_chart_pymes.xml index 72cfc17853c2a..223d80e319309 100644 --- a/addons/l10n_es/account_chart_pymes.xml +++ b/addons/l10n_es/account_chart_pymes.xml @@ -11465,12 +11465,5 @@ - - Plantilla PGCE PYMES 2008 - account.chart.template - default - - - diff --git a/addons/l10n_es/images/config_chart_l10n_es.jpeg b/addons/l10n_es/images/config_chart_l10n_es.jpeg new file mode 100644 index 0000000000000..f62477f95d9e4 Binary files /dev/null and b/addons/l10n_es/images/config_chart_l10n_es.jpeg differ diff --git a/addons/l10n_es/images/l10n_es_chart.jpeg b/addons/l10n_es/images/l10n_es_chart.jpeg new file mode 100644 index 0000000000000..95ea7ee919568 Binary files /dev/null and b/addons/l10n_es/images/l10n_es_chart.jpeg differ diff --git a/addons/l10n_es/taxes_data_assoc.xml b/addons/l10n_es/taxes_data_assoc.xml index 152b27363af6e..e2ebb47fb59f7 100644 --- a/addons/l10n_es/taxes_data_assoc.xml +++ b/addons/l10n_es/taxes_data_assoc.xml @@ -44,6 +44,7 @@ Base adquisiciones exentas + -- 1.0 @@ -52,6 +53,7 @@ Base ventas exentas -- + 1.0 diff --git a/addons/l10n_et/__init__.pyc_dis b/addons/l10n_et/__init__.pyc_dis new file mode 100644 index 0000000000000..441da0ca13e92 --- /dev/null +++ b/addons/l10n_et/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_et/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_fr/__init__.pyc_dis b/addons/l10n_fr/__init__.pyc_dis new file mode 100644 index 0000000000000..97227ee2f1f66 --- /dev/null +++ b/addons/l10n_fr/__init__.pyc_dis @@ -0,0 +1,4 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_fr/__init__.py +import l10n_fr +import report +import wizard \ No newline at end of file diff --git a/addons/l10n_fr/fr_fiscal_templates.xml b/addons/l10n_fr/fr_fiscal_templates.xml index cca96422c22b8..a7e202d47b8d0 100644 --- a/addons/l10n_fr/fr_fiscal_templates.xml +++ b/addons/l10n_fr/fr_fiscal_templates.xml @@ -2,12 +2,6 @@ - - - - @@ -34,38 +28,38 @@ - + - + - - + + - + - + - + - + @@ -76,7 +70,7 @@ - + @@ -87,18 +81,18 @@ - - + + - - + + - + - - + + - + @@ -109,7 +103,7 @@ - + @@ -123,31 +117,31 @@ - + - + - - + + - + - + - + @@ -155,31 +149,31 @@ - + - + - - + + - + - + - + diff --git a/addons/l10n_fr/fr_pcg_taxes.xml b/addons/l10n_fr/fr_pcg_taxes.xml index 636b83e6c01c9..78b34f2d6962c 100644 --- a/addons/l10n_fr/fr_pcg_taxes.xml +++ b/addons/l10n_fr/fr_pcg_taxes.xml @@ -4,11 +4,11 @@ Plan de Taxes France @@ -20,42 +20,24 @@ 1.00 - - Base H.T. 20.0% - TVA collectée 20.0% (Base H.T.) - - 1.00 - Base H.T. 19.6% TVA collectée 19.6% (Base H.T.) 1.00 - + Base H.T. 8.5% TVA collectée 8.5% (Base H.T.) 1.00 - - Base H.T. 10.0% - TVA collectée 10.0% (Base H.T.) - - 1.00 - Base H.T. 7.0% TVA collectée 7.0% (Base H.T.) 1.00 - - Base H.T. 5.0% - TVA collectée 5.0% (Base H.T.) - - 1.00 - Base H.T. 5.5% TVA collectée 5.5% (Base H.T.) @@ -75,12 +57,6 @@ 1.00 - - TVA 20.0% - TVA collectée 20.0% - - 1.00 - TVA 19.6% TVA collectée 19.6% @@ -92,12 +68,6 @@ TVA collectée 8.5% 1.00 - - - TVA 10.0% - TVA collectée 10.0% - - 1.00 TVA 7.0% @@ -105,12 +75,6 @@ 1.00 - - TVA 5.0% - TVA collectée 5.0% - - 1.00 - TVA 5.5% TVA collectée 5.5% @@ -124,18 +88,14 @@ 1.00 + + Base H.T. TVA acquittée c) 1.00 - - Base H.T. 20.0% - TVA acquittée 20.0% (Base H.T.) - - 1.00 - Base H.T. 19.6% TVA acquittée 19.6% (Base H.T.) @@ -148,24 +108,12 @@ 1.00 - - Base H.T. 10.0% - TVA acquittée 10.0% (Base H.T.) - - 1.00 - Base H.T. 7.0% TVA acquittée 7.0% (Base H.T.) 1.00 - - Base H.T. 5.0% - TVA acquittée 5.0% (Base H.T.) - - 1.00 - Base H.T. 5.5% TVA acquittée 5.5% (Base H.T.) @@ -185,12 +133,6 @@ 1.00 - - TVA 20.0% - TVA acquittée 20.0% - - 1.00 - TVA 19.6% TVA acquittée 19.6% @@ -203,24 +145,12 @@ 1.00 - - TVA 10.0% - TVA acquittée 10.0% - - 1.00 - TVA 7.0% TVA acquittée 7.0% 1.00 - - TVA 5.0% - TVA acquittée 5.0% - - 1.00 - TVA 5.5% TVA acquittée 5.5% @@ -241,13 +171,7 @@ e) 1.00 - - - Base H.T. 20.0% - TVA acquittée sur immobilisations 20.0% (Base H.T.) - - 1.00 - + Base H.T. 19.6% TVA acquittée sur immobilisations 19.6% (Base H.T.) @@ -260,24 +184,12 @@ 1.00 - - Base H.T. 10.0% - TVA acquittée sur immobilisations 10.0% (Base H.T.) - - 1.00 - Base H.T. 7.0% TVA acquittée sur immobilisations 7.0% (Base H.T.) 1.00 - - Base H.T. 5.0% - TVA acquittée sur immobilisations 5.0% (Base H.T.) - - 1.00 - Base H.T. 5.5% TVA acquittée sur immobilisations 5.5% (Base H.T.) @@ -297,13 +209,7 @@ f) 1.00 - - - TVA 20.0% - TVA acquittée sur immobilisations 20.0% - - 1.00 - + TVA 19.6% TVA acquittée sur immobilisations 19.6% @@ -316,24 +222,12 @@ 1.00 - - TVA 10.0% - TVA acquittée sur immobilisations 10.0% - - 1.00 - TVA 7.0% TVA acquittée sur immobilisations 7.0% 1.00 - - TVA 5.0% - TVA acquittée sur immobilisations 5.0% - - 1.00 - TVA 5.5% TVA acquittée sur immobilisations 5.5% @@ -352,13 +246,7 @@ g) 1.00 - - - Base H.T. 20.0% - TVA due intracommunautaire 20.0% (Base H.T.) - - 1.00 - + Base H.T. 19.6% TVA due intracommunautaire 19.6% (Base H.T.) @@ -371,24 +259,12 @@ 1.00 - - Base H.T. 10.0% - TVA due intracommunautaire 10.0% (Base H.T.) - - 1.00 - Base H.T. 7.0% TVA due intracommunautaire 7.0% (Base H.T.) 1.00 - - Base H.T. 5.0% - TVA due intracommunautaire 5.0% (Base H.T.) - - 1.00 - Base H.T. 5.5% TVA due intracommunautaire 5.5% (Base H.T.) @@ -407,13 +283,7 @@ h) 1.00 - - - TVA 20.0% - TVA due intracommunautaire 20.0% - - 1.00 - + TVA 19.6% TVA due intracommunautaire 19.6% @@ -426,24 +296,12 @@ 1.00 - - TVA 10.0% - TVA due intracommunautaire 10.0% - - 1.00 - TVA 7.0% TVA due intracommunautaire 7.0% 1.00 - - TVA 5.0% - TVA due intracommunautaire 5.0% - - 1.00 - TVA 5.5% TVA due intracommunautaire 5.5% @@ -463,13 +321,7 @@ i) 1.00 - - - Base H.T. 20.0% - TVA déductible intracommunautaire 20.0% (Base H.T.) - - 1.00 - + Base H.T. 19.6% TVA déductible intracommunautaire 19.6% (Base H.T.) @@ -482,24 +334,12 @@ 1.00 - - Base H.T. 10.0% - TVA déductible intracommunautaire 10.0% (Base H.T.) - - 1.00 - Base H.T. 7.0% TVA déductible intracommunautaire 7.0% (Base H.T.) 1.00 - - Base H.T. 5.0% - TVA déductible intracommunautaire 5.0% (Base H.T.) - - 1.00 - Base H.T. 5.5% TVA déductible intracommunautaire 5.5% (Base H.T.) @@ -518,13 +358,7 @@ j) 1.00 - - - TVA 20.0% - TVA déductible intracommunautaire 20.0% - - 1.00 - + TVA 19.6% TVA déductible intracommunautaire 19.6% @@ -537,24 +371,12 @@ 1.00 - - TVA 10.0% - TVA déductible intracommunautaire 10.0% - - 1.00 - TVA 7.0% TVA déductible intracommunautaire 7.0% 1.00 - - TVA 5.0% - TVA déductible intracommunautaire 5.0% - - 1.00 - TVA 5.5% TVA déductible intracommunautaire 5.5% @@ -620,13 +442,6 @@ - - Plan Comptable Général (France) - account.chart.template - default - - - diff --git a/addons/l10n_fr/fr_tax.xml b/addons/l10n_fr/fr_tax.xml index 22e1e12f03ce8..7cd3a36f54a92 100644 --- a/addons/l10n_fr/fr_tax.xml +++ b/addons/l10n_fr/fr_tax.xml @@ -10,23 +10,23 @@ - TVA collectée (vente) 20,0% - 20.0 - + TVA collectée (vente) 19,6% + 19.6 + percent - + - + - + - + - + sale @@ -52,23 +52,23 @@ sale - + - TVA collectée (vente) 10,0% - 10.0 - + TVA collectée (vente) 7,0% + 7.0 + percent - + - + - - + + - + - + sale @@ -85,8 +85,8 @@ - - + + @@ -122,23 +122,23 @@ - TVA déductible (achat) 20,0% - ACH-20.0 - + TVA déductible (achat) 19,6% + ACH-19.6 + percent - + - + - - + + - + - + - + purchase @@ -164,23 +164,23 @@ purchase - + - TVA déductible (achat) 10,0% - ACH-10.0 - + TVA déductible (achat) 7,0% + ACH-7.0 + percent - + - + - + - + purchase @@ -234,22 +234,22 @@ - TVA déductible (achat) 20,0% TTC - ACH-20.0-TTC + TVA déductible (achat) 19,6% TTC + ACH-19.6-TTC - + percent - + - + - + - + purchase @@ -278,24 +278,24 @@ purchase - + - TVA déductible (achat) 10,0% TTC - ACH-10.0-TTC + TVA déductible (achat) 7,0% TTC + ACH-7.0-TTC - + percent - + - + - + - + purchase @@ -352,21 +352,21 @@ - TVA déd./immobilisation (achat) 20,0% - IMMO-20.0 - + TVA déd./immobilisation (achat) 19,6% + IMMO-19.6 + percent - + - + - + - + purchase @@ -394,23 +394,23 @@ purchase - + - TVA déd./immobilisation (achat) 10,0% - IMMO-10.0 - + TVA déd./immobilisation (achat) 7,0% + IMMO-7.0 + percent - + - + - + - + purchase @@ -464,21 +464,21 @@ - TVA due s/ acq. intracommunautaire (achat) 20,0% - ACH_UE_due-20.0 - + TVA due s/ acq. intracommunautaire (achat) 19,6% + ACH_UE_due-19.6 + percent - + - + - + - + purchase @@ -506,23 +506,23 @@ purchase - + - TVA due s/ acq. intracommunautaire (achat) 10,0% - ACH_UE_due-10.0 - + TVA due s/ acq. intracommunautaire (achat) 7,0% + ACH_UE_due-7.0 + percent - + - + - - + + - + - + purchase @@ -539,8 +539,8 @@ - - + + @@ -576,17 +576,17 @@ - TVA déd. s/ acq. intracommunautaire (achat) 20,0% - ACH_UE_ded.-20.0 - + TVA déd. s/ acq. intracommunautaire (achat) 19,6% + ACH_UE_ded.-19.6 + percent - + - + purchase @@ -610,19 +610,19 @@ purchase - + - TVA déd. s/ acq. intracommunautaire (achat) 10,0% - ACH_UE_ded.-10.0 - + TVA déd. s/ acq. intracommunautaire (achat) 7,0% + ACH_UE_ded.-7.0 + percent - + - + purchase diff --git a/addons/l10n_fr/images/config_chart_l10n_fr.jpeg b/addons/l10n_fr/images/config_chart_l10n_fr.jpeg new file mode 100644 index 0000000000000..b12086501dfda Binary files /dev/null and b/addons/l10n_fr/images/config_chart_l10n_fr.jpeg differ diff --git a/addons/l10n_fr/images/l10n_fr_chart.jpeg b/addons/l10n_fr/images/l10n_fr_chart.jpeg new file mode 100644 index 0000000000000..c38f031b66ca0 Binary files /dev/null and b/addons/l10n_fr/images/l10n_fr_chart.jpeg differ diff --git a/addons/l10n_fr/l10n_fr.pyc_dis b/addons/l10n_fr/l10n_fr.pyc_dis new file mode 100644 index 0000000000000..9bedfeb8e6809 --- /dev/null +++ b/addons/l10n_fr/l10n_fr.pyc_dis @@ -0,0 +1,33 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_fr/l10n_fr.py +from openerp.osv import fields, osv + +class l10n_fr_report(osv.osv): + _name = 'l10n.fr.report' + _description = 'Report for l10n_fr' + _columns = {'code': fields.char('Code', size=64), + 'name': fields.char('Name', size=128), + 'line_ids': fields.one2many('l10n.fr.line', 'report_id', 'Lines')} + _sql_constraints = [('code_uniq', 'unique (code)', 'The code report must be unique !')] + + +l10n_fr_report() + +class l10n_fr_line(osv.osv): + _name = 'l10n.fr.line' + _description = 'Report Lines for l10n_fr' + _columns = {'code': fields.char('Variable Name', size=64), + 'definition': fields.char('Definition', size=512), + 'name': fields.char('Name', size=256), + 'report_id': fields.many2one('l10n.fr.report', 'Report')} + _sql_constraints = [('code_uniq', 'unique (code)', 'The variable name must be unique !')] + + +l10n_fr_line() + +class res_company(osv.osv): + _inherit = 'res.company' + _columns = {'siret': fields.char('SIRET', size=64), + 'ape': fields.char('APE', size=64)} + + +res_company() \ No newline at end of file diff --git a/addons/l10n_fr/plan_comptable_general.xml b/addons/l10n_fr/plan_comptable_general.xml index a029e101d07e4..c68e0a88a4ca6 100644 --- a/addons/l10n_fr/plan_comptable_general.xml +++ b/addons/l10n_fr/plan_comptable_general.xml @@ -3584,7 +3584,7 @@ - TVA due intracommunautaire (Taux Normal) + TVA due intracommunautaire 19,6% 445201 other @@ -3592,7 +3592,7 @@ - TVA due intracommunautaire (Taux Intermédiaire) + TVA due intracommunautaire 5,5% 445202 other @@ -3600,7 +3600,7 @@ - TVA due intracommunautaire (Autre taux) + TVA due intracommunautaire (autre taux) 445203 other @@ -3712,7 +3712,7 @@ - TVA collectée (Taux Normal) + TVA collectée 19,6% 445711 other @@ -3720,7 +3720,7 @@ - TVA collectée (Taux Intermédiaire) + TVA collectée 5,5% 445712 other @@ -3728,7 +3728,7 @@ - TVA collectée (Autre taux) + TVA collectée (autre taux) 445713 other diff --git a/addons/l10n_fr/report/__init__.pyc_dis b/addons/l10n_fr/report/__init__.pyc_dis new file mode 100644 index 0000000000000..984321ebd022e --- /dev/null +++ b/addons/l10n_fr/report/__init__.pyc_dis @@ -0,0 +1,4 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_fr/report/__init__.py +import base_report +import bilan_report +import compute_resultant_report \ No newline at end of file diff --git a/addons/l10n_fr/report/base_report.pyc_dis b/addons/l10n_fr/report/base_report.pyc_dis new file mode 100644 index 0000000000000..4fe1792ab90c4 --- /dev/null +++ b/addons/l10n_fr/report/base_report.pyc_dis @@ -0,0 +1,87 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_fr/report/base_report.py +import time +from openerp.report import report_sxw + +class base_report(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context = None): + super(base_report, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time, + '_load': self._load, + '_get_variable': self._get_variable, + '_set_variable': self._set_variable}) + self.context = context + + def _load(self, name, form): + fiscalyear = self.pool.get('account.fiscalyear').browse(self.cr, self.uid, form['fiscalyear_id']) + period_ids = self.pool.get('account.period').search(self.cr, self.uid, [('fiscalyear_id', '=', form['fiscalyear_id'])]) + if period_ids: + self.cr.execute('SELECT MIN(date_start) AS date_start, MAX(date_stop) AS date_stop FROM account_period WHERE id = ANY(%s)', (period_ids,)) + dates = self.cr.dictfetchall() + else: + dates = False + if dates: + self._set_variable('date_start', dates[0]['date_start']) + self._set_variable('date_stop', dates[0]['date_stop']) + self.cr.execute('SELECT l10n_fr_line.code,definition FROM l10n_fr_line LEFT JOIN l10n_fr_report ON l10n_fr_report.id=report_id WHERE l10n_fr_report.code=%s', (name,)) + datas = self.cr.dictfetchall() + for line in datas: + self._load_accounts(form, line['code'], eval(line['definition']), fiscalyear, period_ids) + + def _set_variable(self, variable, valeur): + self.localcontext.update({variable: valeur}) + + def _get_variable(self, variable): + return self.localcontext[variable] + + def _load_accounts(self, form, code, definition, fiscalyear, period_ids): + accounts = {} + for x in definition['load']: + p = x.split(':') + accounts[p[1]] = [p[0], p[2]] + + sum = 0.0 + if fiscalyear.state != 'done' or not code.startswith('bpcheck'): + query_params = [] + query_cond = '(' + for account in accounts: + query_cond += "aa.code LIKE '" + account + "%%' OR " + + query_cond = query_cond[:-4] + ')' + if len(definition['except']) > 0: + query_cond = query_cond + ' and (' + for account in definition['except']: + query_cond += "aa.code NOT LIKE '" + account + "%%' AND " + + query_cond = query_cond[:-5] + ')' + closed_cond = '' + if fiscalyear.state == 'done': + closed_cond = " AND (aml.move_id NOT IN (SELECT account_move.id as move_id FROM account_move WHERE period_id = ANY(%s) AND journal_id=(SELECT res_id FROM ir_model_data WHERE name='closing_journal' AND module='l10n_fr')) OR (aa.type != 'income' AND aa.type !='expense'))" + query_params.append(list(period_ids)) + query = 'SELECT aa.code AS code, SUM(debit) as debit, SUM(credit) as credit FROM account_move_line aml LEFT JOIN account_account aa ON aa.id=aml.account_id WHERE ' + query_cond + closed_cond + " AND aml.state='valid' AND aml.period_id = ANY(%s) GROUP BY code" + query_params.append(list(period_ids)) + self.cr.execute(query, query_params) + lines = self.cr.dictfetchall() + for line in lines: + for account in accounts: + if line['code'].startswith(account): + operator = accounts[account][0] + type = accounts[account][1] + value = 0.0 + if type == 'S': + value = line['debit'] - line['credit'] + elif type == 'D': + value = line['debit'] - line['credit'] + if abs(value) < 0.001: + value = 0.0 + elif type == 'C': + value = line['credit'] - line['debit'] + if abs(value) < 0.001: + value = 0.0 + if operator == '+': + sum += value + else: + sum -= value + break + + self._set_variable(code, sum) \ No newline at end of file diff --git a/addons/l10n_fr/report/bilan_report.pyc_dis b/addons/l10n_fr/report/bilan_report.pyc_dis new file mode 100644 index 0000000000000..5357a901f106d --- /dev/null +++ b/addons/l10n_fr/report/bilan_report.pyc_dis @@ -0,0 +1,11 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_fr/report/bilan_report.py +import base_report +from openerp.report import report_sxw + +class bilan(base_report.base_report): + + def __init__(self, cr, uid, name, context): + super(bilan, self).__init__(cr, uid, name, context) + + +report_sxw.report_sxw('report.l10n.fr.bilan', 'account.move.line', 'addons/l10n_fr/report/bilan_report.rml', parser=bilan, header=False) \ No newline at end of file diff --git a/addons/l10n_fr/report/compute_resultant_report.pyc_dis b/addons/l10n_fr/report/compute_resultant_report.pyc_dis new file mode 100644 index 0000000000000..c18bfb7fe997c --- /dev/null +++ b/addons/l10n_fr/report/compute_resultant_report.pyc_dis @@ -0,0 +1,11 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_fr/report/compute_resultant_report.py +import base_report +from openerp.report import report_sxw + +class cdr(base_report.base_report): + + def __init__(self, cr, uid, name, context): + super(cdr, self).__init__(cr, uid, name, context) + + +report_sxw.report_sxw('report.l10n.fr.compute_resultant', 'account.move.line', 'addons/l10n_fr/report/compute_resultant_report.rml', parser=cdr, header=False) \ No newline at end of file diff --git a/addons/l10n_fr/wizard/__init__.pyc_dis b/addons/l10n_fr/wizard/__init__.pyc_dis new file mode 100644 index 0000000000000..3c67677b6f183 --- /dev/null +++ b/addons/l10n_fr/wizard/__init__.pyc_dis @@ -0,0 +1,3 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_fr/wizard/__init__.py +import fr_report_bilan +import fr_report_compute_resultant \ No newline at end of file diff --git a/addons/l10n_fr/wizard/fr_report_bilan.pyc_dis b/addons/l10n_fr/wizard/fr_report_bilan.pyc_dis new file mode 100644 index 0000000000000..6917024e609f0 --- /dev/null +++ b/addons/l10n_fr/wizard/fr_report_bilan.pyc_dis @@ -0,0 +1,26 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_fr/wizard/fr_report_bilan.py +from openerp.osv import fields, osv + +class account_bilan_report(osv.osv_memory): + _name = 'account.bilan.report' + _description = 'Account Bilan Report' + + def _get_default_fiscalyear(self, cr, uid, context = None): + fiscalyear_id = self.pool.get('account.fiscalyear').find(cr, uid) + return fiscalyear_id + + _columns = {'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True)} + _defaults = {'fiscalyear_id': _get_default_fiscalyear} + + def print_bilan_report(self, cr, uid, ids, context = None): + active_ids = context.get('active_ids', []) + data = {} + data['form'] = {} + data['ids'] = active_ids + data['form']['fiscalyear_id'] = self.browse(cr, uid, ids)[0].fiscalyear_id.id + return {'type': 'ir.actions.report.xml', + 'report_name': 'l10n.fr.bilan', + 'datas': data} + + +account_bilan_report() \ No newline at end of file diff --git a/addons/l10n_fr/wizard/fr_report_compute_resultant.pyc_dis b/addons/l10n_fr/wizard/fr_report_compute_resultant.pyc_dis new file mode 100644 index 0000000000000..93909239559fb --- /dev/null +++ b/addons/l10n_fr/wizard/fr_report_compute_resultant.pyc_dis @@ -0,0 +1,26 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_fr/wizard/fr_report_compute_resultant.py +from openerp.osv import fields, osv + +class account_cdr_report(osv.osv_memory): + _name = 'account.cdr.report' + _description = 'Account CDR Report' + + def _get_defaults(self, cr, uid, context = None): + fiscalyear_id = self.pool.get('account.fiscalyear').find(cr, uid) + return fiscalyear_id + + _columns = {'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True)} + _defaults = {'fiscalyear_id': _get_defaults} + + def print_cdr_report(self, cr, uid, ids, context = None): + active_ids = context.get('active_ids', []) + data = {} + data['form'] = {} + data['ids'] = active_ids + data['form']['fiscalyear_id'] = self.browse(cr, uid, ids)[0].fiscalyear_id.id + return {'type': 'ir.actions.report.xml', + 'report_name': 'l10n.fr.compute_resultant', + 'datas': data} + + +account_cdr_report() \ No newline at end of file diff --git a/addons/l10n_fr_hr_payroll/report/fiche_paye.py b/addons/l10n_fr_hr_payroll/report/fiche_paye.py old mode 100755 new mode 100644 diff --git a/addons/l10n_fr_hr_payroll/report/fiche_paye.rml b/addons/l10n_fr_hr_payroll/report/fiche_paye.rml index 3cd5059770a2d..2d1a6bff8b287 100644 --- a/addons/l10n_fr_hr_payroll/report/fiche_paye.rml +++ b/addons/l10n_fr_hr_payroll/report/fiche_paye.rml @@ -244,7 +244,7 @@ [[o.contract_id.qualif or '_']] [[o.contract_id.niveau or '_']] [[o.contract_id.coef or '_']] - [[ formatLang(o.contract_id.date_start, date=True) or '_']] Sortie : [[ formatLang(o.contract_id.date_end, date=True) or '_']] + [[o.contract_id.date_start or '_']] Sortie : [[o.contract_id.date_end or '']] diff --git a/addons/l10n_gr/__init__.pyc_dis b/addons/l10n_gr/__init__.pyc_dis new file mode 100644 index 0000000000000..7272b332f0efc --- /dev/null +++ b/addons/l10n_gr/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_gr/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_gr/account_tax.xml b/addons/l10n_gr/account_tax.xml index e60b3909a69c5..c3de033869b1f 100644 --- a/addons/l10n_gr/account_tax.xml +++ b/addons/l10n_gr/account_tax.xml @@ -22,13 +22,7 @@ - - Πρότυπο Ελληνικού Λογιστικού Σχεδίου - account.chart.template - default - - - + Υπόλοιπο ΦΠΑ diff --git a/addons/l10n_gr/images/config_chart_l10n_gr.jpeg b/addons/l10n_gr/images/config_chart_l10n_gr.jpeg new file mode 100644 index 0000000000000..f11e0d8c12ac0 Binary files /dev/null and b/addons/l10n_gr/images/config_chart_l10n_gr.jpeg differ diff --git a/addons/l10n_gr/images/l10n_gr_chart.jpeg b/addons/l10n_gr/images/l10n_gr_chart.jpeg new file mode 100644 index 0000000000000..1ae7279d65e6e Binary files /dev/null and b/addons/l10n_gr/images/l10n_gr_chart.jpeg differ diff --git a/addons/l10n_gt/__init__.pyc_dis b/addons/l10n_gt/__init__.pyc_dis new file mode 100644 index 0000000000000..52e6a738c0808 --- /dev/null +++ b/addons/l10n_gt/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_gt/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_gt/images/config_chart_l10n_gt.jpeg b/addons/l10n_gt/images/config_chart_l10n_gt.jpeg new file mode 100644 index 0000000000000..cd7eca8d8c5cc Binary files /dev/null and b/addons/l10n_gt/images/config_chart_l10n_gt.jpeg differ diff --git a/addons/l10n_gt/images/l10n_gt_chart.jpeg b/addons/l10n_gt/images/l10n_gt_chart.jpeg new file mode 100644 index 0000000000000..86f98ca54cda8 Binary files /dev/null and b/addons/l10n_gt/images/l10n_gt_chart.jpeg differ diff --git a/addons/l10n_gt/l10n_gt_base.xml b/addons/l10n_gt/l10n_gt_base.xml index e14d5910583ff..8caf6ebe48cd1 100644 --- a/addons/l10n_gt/l10n_gt_base.xml +++ b/addons/l10n_gt/l10n_gt_base.xml @@ -32,13 +32,6 @@ - - Plantilla de cuentas de Guatemala (sencilla) - account.chart.template - default - - - diff --git a/addons/l10n_hn/__init__.pyc_dis b/addons/l10n_hn/__init__.pyc_dis new file mode 100644 index 0000000000000..058149f4aa311 --- /dev/null +++ b/addons/l10n_hn/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_hn/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_hn/l10n_hn_base.xml b/addons/l10n_hn/l10n_hn_base.xml index 9f5a9b8cbd932..b8b85c3f2a796 100644 --- a/addons/l10n_hn/l10n_hn_base.xml +++ b/addons/l10n_hn/l10n_hn_base.xml @@ -18,13 +18,6 @@ - - Plantilla de cuentas de Honduras (sencilla) - account.chart.template - default - - - diff --git a/addons/l10n_hr/__init__.pyc_dis b/addons/l10n_hr/__init__.pyc_dis new file mode 100644 index 0000000000000..a7e50e1d6743f --- /dev/null +++ b/addons/l10n_hr/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_hr/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_hr/l10n_hr_chart_template.xml b/addons/l10n_hr/l10n_hr_chart_template.xml index 9509030117ec9..d9a7db5710cbb 100644 --- a/addons/l10n_hr/l10n_hr_chart_template.xml +++ b/addons/l10n_hr/l10n_hr_chart_template.xml @@ -16,14 +16,6 @@ - - - RRIF-ov računski plan za poduzetnike - account.chart.template - default - - - diff --git a/addons/l10n_in/__init__.pyc_dis b/addons/l10n_in/__init__.pyc_dis new file mode 100644 index 0000000000000..f758af9e11548 --- /dev/null +++ b/addons/l10n_in/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_in/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_in/images/config_chart_l10n_in.jpeg b/addons/l10n_in/images/config_chart_l10n_in.jpeg new file mode 100644 index 0000000000000..ea9be3af1b516 Binary files /dev/null and b/addons/l10n_in/images/config_chart_l10n_in.jpeg differ diff --git a/addons/l10n_in/images/l10n_in_chart.jpeg b/addons/l10n_in/images/l10n_in_chart.jpeg new file mode 100644 index 0000000000000..c8b894e900d48 Binary files /dev/null and b/addons/l10n_in/images/l10n_in_chart.jpeg differ diff --git a/addons/l10n_in/l10n_in_private_chart.xml b/addons/l10n_in/l10n_in_private_chart.xml index 91fc525e070b1..671fdc334d910 100644 --- a/addons/l10n_in/l10n_in_private_chart.xml +++ b/addons/l10n_in/l10n_in_private_chart.xml @@ -515,13 +515,7 @@ - - India - Chart of Accounts for Private Ltd/Partnership - account.chart.template - default - - - + diff --git a/addons/l10n_in/l10n_in_public_chart.xml b/addons/l10n_in/l10n_in_public_chart.xml index f30a9173c3fd9..9612afddc39ff 100644 --- a/addons/l10n_in/l10n_in_public_chart.xml +++ b/addons/l10n_in/l10n_in_public_chart.xml @@ -669,13 +669,6 @@ - - India - Chart of Accounts for Public Ltd - account.chart.template - default - - - diff --git a/addons/l10n_in_hr_payroll/__init__.pyc_dis b/addons/l10n_in_hr_payroll/__init__.pyc_dis new file mode 100644 index 0000000000000..7fdf287e05409 --- /dev/null +++ b/addons/l10n_in_hr_payroll/__init__.pyc_dis @@ -0,0 +1,4 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/__init__.py +import l10n_in_hr_payroll +import report +import wizard \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv b/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv index 043ba8f0522d9..2fe1ba739f4af 100644 --- a/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv +++ b/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv @@ -1,5 +1,5 @@ -"id","amount_select","condition_range_min","condition_range_max","amount_percentage","amount_fix","name","category_id/id","sequence","code","parent_rule_id/id","condition_select","condition_range","amount_percentage_base" -1,"fix",1,1,,100,"Education Allowance For One Child",hr_payroll.ALW,23,"CHEAONE","hr_payroll_rule_child1","range","employee.children", -2,"fix",2,10,,200,"Education Allowance For Two Child",hr_payroll.ALW,24,"CHEATWO","hr_payroll_rule_child1","range","employee.children", -3,"fix",1,1,,300,"Child Hostel Allowance For One Child",hr_payroll.ALW,26,"CHOONE","hr_payroll_rule_child2","range","employee.children", -4,"fix",2,10,,600,"Child Hostel Allowance For Two Child",hr_payroll.ALW,27,"CHOTWO","hr_payroll_rule_child2","range","employee.children", +"id","amount_select","condition_range_min","condition_range_max","amount_percentage","amount_fix","name","category_id","sequence","code","parent_rule_id/id","condition_select","condition_range","amount_percentage_base" +1,"fix",1,1,,100,"Education Allowance For One Child","Allowance",23,"CHEAONE","hr_payroll_rule_child1","range","employee.children", +2,"fix",2,10,,200,"Education Allowance For Two Child","Allowance",24,"CHEATWO","hr_payroll_rule_child1","range","employee.children", +3,"fix",1,1,,300,"Child Hostel Allowance For One Child","Allowance",26,"CHOONE","hr_payroll_rule_child2","range","employee.children", +4,"fix",2,10,,600,"Child Hostel Allowance For Two Child","Allowance",27,"CHOTWO","hr_payroll_rule_child2","range","employee.children", diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.pyc_dis b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.pyc_dis new file mode 100644 index 0000000000000..2583d79e8e689 --- /dev/null +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.pyc_dis @@ -0,0 +1,235 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py +import time +from datetime import datetime +from dateutil.relativedelta import relativedelta +from calendar import isleap +from openerp.tools.translate import _ +from openerp.osv import fields, osv +from openerp import netsvc +import openerp.addons.decimal_precision as dp +DATETIME_FORMAT = '%Y-%m-%d' + +class hr_contract(osv.osv): + """ + Employee contract allows to add different values in fields. + Fields are used in salary rule computation. + """ + _inherit = 'hr.contract' + _description = 'HR Contract' + _columns = {'tds': fields.float('TDS', digits_compute=dp.get_precision('Payroll'), help='Amount for Tax Deduction at Source'), + 'driver_salay': fields.boolean('Driver Salary', help='Check this box if you provide allowance for driver'), + 'medical_insurance': fields.float('Medical Insurance', digits_compute=dp.get_precision('Payroll'), help='Deduction towards company provided medical insurance'), + 'voluntary_provident_fund': fields.float('Voluntary Provident Fund (%)', digits_compute=dp.get_precision('Payroll'), help='VPF is a safe option wherein you can contribute more than the PF ceiling of 12% that has been mandated by the government and VPF computed as percentage(%)'), + 'house_rent_allowance_metro_nonmetro': fields.float('House Rent Allowance (%)', digits_compute=dp.get_precision('Payroll'), help='HRA is an allowance given by the employer to the employee for taking care of his rental or accommodation expenses for metro city it is 50 % and for non metro 40%.HRA computed as percentage(%)'), + 'supplementary_allowance': fields.float('Supplementary Allowance', digits_compute=dp.get_precision('Payroll'))} + + +hr_contract() + +class payroll_advice(osv.osv): + """ + Bank Advice + """ + _name = 'hr.payroll.advice' + _description = 'Bank Advice' + _columns = {'name': fields.char('Name', size=32, readonly=True, required=True, states={'draft': [('readonly', False)]}), + 'note': fields.text('Description'), + 'date': fields.date('Date', readonly=True, required=True, states={'draft': [('readonly', False)]}, help='Advice Date is used to search Payslips'), + 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('cancel', 'Cancelled')], 'Status', select=True, readonly=True), + 'number': fields.char('Reference', size=16, readonly=True), + 'line_ids': fields.one2many('hr.payroll.advice.line', 'advice_id', 'Employee Salary', states={'draft': [('readonly', False)]}, readonly=True), + 'chaque_nos': fields.char('Cheque Numbers', size=256), + 'neft': fields.boolean('NEFT Transaction', help='Check this box if your company use online transfer for salary'), + 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft': [('readonly', False)]}), + 'bank_id': fields.many2one('res.bank', 'Bank', readonly=True, states={'draft': [('readonly', False)]}, help='Select the Bank from which the salary is going to be paid'), + 'batch_id': fields.many2one('hr.payslip.run', 'Batch', readonly=True)} + _defaults = {'date': lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'draft', + 'company_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id, + 'note': 'Please make the payroll transfer from above account number to the below mentioned account numbers towards employee salaries:'} + + def compute_advice(self, cr, uid, ids, context = None): + """ + Advice - Create Advice lines in Payment Advice and + compute Advice lines. + @param cr: the current row, from the database cursor, + @param uid: the current user\xe2\x80\x99s ID for security checks, + @param ids: List of Advice\xe2\x80\x99s IDs + @return: Advice lines + @param context: A standard dictionary for contextual values + """ + payslip_pool = self.pool.get('hr.payslip') + advice_line_pool = self.pool.get('hr.payroll.advice.line') + payslip_line_pool = self.pool.get('hr.payslip.line') + for advice in self.browse(cr, uid, ids, context=context): + old_line_ids = advice_line_pool.search(cr, uid, [('advice_id', '=', advice.id)], context=context) + if old_line_ids: + advice_line_pool.unlink(cr, uid, old_line_ids, context=context) + slip_ids = payslip_pool.search(cr, uid, [('date_from', '<=', advice.date), ('date_to', '>=', advice.date), ('state', '=', 'done')], context=context) + for slip in payslip_pool.browse(cr, uid, slip_ids, context=context): + if not slip.employee_id.bank_account_id and not slip.employee_id.bank_account_id.acc_number: + raise osv.except_osv(_('Error!'), _('Please define bank account for the %s employee') % slip.employee_id.name) + line_ids = payslip_line_pool.search(cr, uid, [('slip_id', '=', slip.id), ('code', '=', 'NET')], context=context) + if line_ids: + line = payslip_line_pool.browse(cr, uid, line_ids, context=context)[0] + advice_line = {'advice_id': advice.id, + 'name': slip.employee_id.bank_account_id.acc_number, + 'employee_id': slip.employee_id.id, + 'bysal': line.total} + advice_line_pool.create(cr, uid, advice_line, context=context) + payslip_pool.write(cr, uid, slip_ids, {'advice_id': advice.id}, context=context) + + return True + + def confirm_sheet(self, cr, uid, ids, context = None): + """ + confirm Advice - confirmed Advice after computing Advice Lines.. + @param cr: the current row, from the database cursor, + @param uid: the current user\xe2\x80\x99s ID for security checks, + @param ids: List of confirm Advice\xe2\x80\x99s IDs + @return: confirmed Advice lines and set sequence of Advice. + @param context: A standard dictionary for contextual values + """ + seq_obj = self.pool.get('ir.sequence') + for advice in self.browse(cr, uid, ids, context=context): + if not advice.line_ids: + raise osv.except_osv(_('Error!'), _('You can not confirm Payment advice without advice lines.')) + advice_date = datetime.strptime(advice.date, DATETIME_FORMAT) + advice_year = advice_date.strftime('%m') + '-' + advice_date.strftime('%Y') + number = seq_obj.get(cr, uid, 'payment.advice') + sequence_num = 'PAY/' + advice_year + '/' + number + self.write(cr, uid, [advice.id], {'number': sequence_num, + 'state': 'confirm'}, context=context) + + return True + + def set_to_draft(self, cr, uid, ids, context = None): + """Resets Advice as draft. + """ + return self.write(cr, uid, ids, {'state': 'draft'}, context=context) + + def cancel_sheet(self, cr, uid, ids, context = None): + """Marks Advice as cancelled. + """ + return self.write(cr, uid, ids, {'state': 'cancel'}, context=context) + + def onchange_company_id(self, cr, uid, ids, company_id = False, context = None): + res = {} + if company_id: + company = self.pool.get('res.company').browse(cr, uid, [company_id], context=context)[0] + if company.partner_id.bank_ids: + res.update({'bank_id': company.partner_id.bank_ids[0].bank.id}) + return {'value': res} + + +payroll_advice() + +class hr_payslip_run(osv.osv): + _inherit = 'hr.payslip.run' + _description = 'Payslip Batches' + _columns = {'available_advice': fields.boolean('Made Payment Advice?', help='If this box is checked which means that Payment Advice exists for current batch', readonly=False)} + + def copy(self, cr, uid, id, default = {}, context = None): + if not default: + default = {} + default.update({'available_advice': False}) + return super(hr_payslip_run, self).copy(cr, uid, id, default, context=context) + + def draft_payslip_run(self, cr, uid, ids, context = None): + res = super(hr_payslip_run, self).draft_payslip_run(cr, uid, ids, context=context) + self.write(cr, uid, ids, {'available_advice': False}, context=context) + return res + + def create_advice(self, cr, uid, ids, context = None): + wf_service = netsvc.LocalService('workflow') + payslip_pool = self.pool.get('hr.payslip') + payslip_line_pool = self.pool.get('hr.payslip.line') + advice_pool = self.pool.get('hr.payroll.advice') + advice_line_pool = self.pool.get('hr.payroll.advice.line') + users = self.pool.get('res.users').browse(cr, uid, [uid], context=context) + for run in self.browse(cr, uid, ids, context=context): + if run.available_advice: + raise osv.except_osv(_('Error!'), _("Payment advice already exists for %s, 'Set to Draft' to create a new advice.") % run.name) + advice_data = {'batch_id': run.id, + 'company_id': users[0].company_id.id, + 'name': run.name, + 'date': run.date_end, + 'bank_id': users[0].company_id.bank_ids and users[0].company_id.bank_ids[0].id or False} + advice_id = advice_pool.create(cr, uid, advice_data, context=context) + slip_ids = [] + for slip_id in run.slip_ids: + wf_service.trg_validate(uid, 'hr.payslip', slip_id.id, 'hr_verify_sheet', cr) + wf_service.trg_validate(uid, 'hr.payslip', slip_id.id, 'process_sheet', cr) + slip_ids.append(slip_id.id) + + for slip in payslip_pool.browse(cr, uid, slip_ids, context=context): + if not slip.employee_id.bank_account_id or not slip.employee_id.bank_account_id.acc_number: + raise osv.except_osv(_('Error!'), _('Please define bank account for the %s employee') % slip.employee_id.name) + line_ids = payslip_line_pool.search(cr, uid, [('slip_id', '=', slip.id), ('code', '=', 'NET')], context=context) + if line_ids: + line = payslip_line_pool.browse(cr, uid, line_ids, context=context)[0] + advice_line = {'advice_id': advice_id, + 'name': slip.employee_id.bank_account_id.acc_number, + 'employee_id': slip.employee_id.id, + 'bysal': line.total} + advice_line_pool.create(cr, uid, advice_line, context=context) + + return self.write(cr, uid, ids, {'available_advice': True}) + + +hr_payslip_run() + +class payroll_advice_line(osv.osv): + """ + Bank Advice Lines + """ + + def onchange_employee_id(self, cr, uid, ids, employee_id = False, context = None): + res = {} + hr_obj = self.pool.get('hr.employee') + if not employee_id: + return {'value': res} + employee = hr_obj.browse(cr, uid, [employee_id], context=context)[0] + res.update({'name': employee.bank_account_id.acc_number, + 'ifsc_code': employee.bank_account_id.bank_bic or ''}) + return {'value': res} + + _name = 'hr.payroll.advice.line' + _description = 'Bank Advice Lines' + _columns = {'advice_id': fields.many2one('hr.payroll.advice', 'Bank Advice'), + 'name': fields.char('Bank Account No.', size=25, required=True), + 'ifsc_code': fields.char('IFSC Code', size=16), + 'employee_id': fields.many2one('hr.employee', 'Employee', required=True), + 'bysal': fields.float('By Salary', digits_compute=dp.get_precision('Payroll')), + 'debit_credit': fields.char('C/D', size=3, required=False), + 'company_id': fields.related('advice_id', 'company_id', type='many2one', required=False, relation='res.company', string='Company', store=True), + 'ifsc': fields.related('advice_id', 'neft', type='boolean', string='IFSC')} + _defaults = {'debit_credit': 'C'} + + +payroll_advice_line() + +class hr_payslip(osv.osv): + """ + Employee Pay Slip + """ + _inherit = 'hr.payslip' + _description = 'Pay Slips' + _columns = {'advice_id': fields.many2one('hr.payroll.advice', 'Bank Advice')} + + def copy(self, cr, uid, id, default = {}, context = None): + if not default: + default = {} + default.update({'advice_id': False}) + return super(hr_payslip, self).copy(cr, uid, id, default, context=context) + + +hr_payslip() + +class res_company(osv.osv): + _inherit = 'res.company' + _columns = {'dearness_allowance': fields.boolean('Dearness Allowance', help='Check this box if your company provide Dearness Allowance to employee')} + _defaults = {'dearness_allowance': True} + + +res_company() \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/__init__.pyc_dis b/addons/l10n_in_hr_payroll/report/__init__.pyc_dis new file mode 100644 index 0000000000000..c4c4414d4e9de --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/__init__.pyc_dis @@ -0,0 +1,7 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/report/__init__.py +import report_payslip_details +import report_payroll_advice +import report_hr_salary_employee_bymonth +import payment_advice_report +import report_hr_yearly_salary_detail +import payslip_report \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/payment_advice_report.pyc_dis b/addons/l10n_in_hr_payroll/report/payment_advice_report.pyc_dis new file mode 100644 index 0000000000000..2a0b905118cfe --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/payment_advice_report.pyc_dis @@ -0,0 +1,42 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/report/payment_advice_report.py +from openerp import tools +from openerp.osv import fields, osv + +class payment_advice_report(osv.osv): + _name = 'payment.advice.report' + _description = 'Payment Advice Analysis' + _auto = False + _columns = {'name': fields.char('Name', size=32, readonly=True), + 'date': fields.date('Date', readonly=True), + 'year': fields.char('Year', size=4, readonly=True), + 'month': fields.selection([('01', 'January'), + ('02', 'February'), + ('03', 'March'), + ('04', 'April'), + ('05', 'May'), + ('06', 'June'), + ('07', 'July'), + ('08', 'August'), + ('09', 'September'), + ('10', 'October'), + ('11', 'November'), + ('12', 'December')], 'Month', readonly=True), + 'day': fields.char('Day', size=128, readonly=True), + 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('cancel', 'Cancelled')], 'Status', select=True, readonly=True), + 'employee_id': fields.many2one('hr.employee', 'Employee', readonly=True), + 'nbr': fields.integer('# Payment Lines', readonly=True), + 'number': fields.char('Number', size=16, readonly=True), + 'bysal': fields.float('By Salary', readonly=True), + 'bank_id': fields.many2one('res.bank', 'Bank', readonly=True), + 'company_id': fields.many2one('res.company', 'Company', readonly=True), + 'cheque_nos': fields.char('Cheque Numbers', size=256, readonly=True), + 'neft': fields.boolean('NEFT Transaction', readonly=True), + 'ifsc_code': fields.char('IFSC Code', size=32, readonly=True), + 'employee_bank_no': fields.char('Employee Bank Account', size=32, required=True)} + + def init(self, cr): + tools.drop_view_if_exists(cr, 'payment_advice_report') + cr.execute("\n create or replace view payment_advice_report as (\n select\n min(l.id) as id,\n sum(l.bysal) as bysal,\n p.name,\n p.state,\n p.date,\n p.number,\n p.company_id,\n p.bank_id,\n p.chaque_nos as cheque_nos,\n p.neft,\n l.employee_id,\n l.ifsc_code,\n l.name as employee_bank_no,\n to_char(p.date, 'YYYY') as year,\n to_char(p.date, 'MM') as month,\n to_char(p.date, 'YYYY-MM-DD') as day,\n 1 as nbr\n from\n hr_payroll_advice as p\n left join hr_payroll_advice_line as l on (p.id=l.advice_id)\n where \n l.employee_id IS NOT NULL\n group by\n p.number,p.name,p.date,p.state,p.company_id,p.bank_id,p.chaque_nos,p.neft,\n l.employee_id,l.advice_id,l.bysal,l.ifsc_code, l.name\n )\n ") + + +payment_advice_report() \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/payslip_report.pyc_dis b/addons/l10n_in_hr_payroll/report/payslip_report.pyc_dis new file mode 100644 index 0000000000000..b5a61eddb833d --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/payslip_report.pyc_dis @@ -0,0 +1,41 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/report/payslip_report.py +from openerp import tools +from openerp.osv import fields, osv + +class payslip_report(osv.osv): + _name = 'payslip.report' + _description = 'Payslip Analysis' + _auto = False + _columns = {'name': fields.char('Name', size=32, readonly=True), + 'date_from': fields.date('Date From', readonly=True), + 'date_to': fields.date('Date To', readonly=True), + 'year': fields.char('Year', size=4, readonly=True), + 'month': fields.selection([('01', 'January'), + ('02', 'February'), + ('03', 'March'), + ('04', 'April'), + ('05', 'May'), + ('06', 'June'), + ('07', 'July'), + ('08', 'August'), + ('09', 'September'), + ('10', 'October'), + ('11', 'November'), + ('12', 'December')], 'Month', readonly=True), + 'day': fields.char('Day', size=128, readonly=True), + 'state': fields.selection([('draft', 'Draft'), ('done', 'Done'), ('cancel', 'Rejected')], 'Status', readonly=True), + 'employee_id': fields.many2one('hr.employee', 'Employee', readonly=True), + 'nbr': fields.integer('# Payslip lines', readonly=True), + 'number': fields.char('Number', size=16, readonly=True), + 'struct_id': fields.many2one('hr.payroll.structure', 'Structure', readonly=True), + 'company_id': fields.many2one('res.company', 'Company', readonly=True), + 'paid': fields.boolean('Made Payment Order ? ', readonly=True), + 'total': fields.float('Total', readonly=True), + 'category_id': fields.many2one('hr.salary.rule.category', 'Category', readonly=True)} + + def init(self, cr): + tools.drop_view_if_exists(cr, 'payslip_report') + cr.execute("\n create or replace view payslip_report as (\n select\n min(l.id) as id,\n l.name,\n p.struct_id,\n p.state,\n p.date_from,\n p.date_to,\n p.number,\n p.company_id,\n p.paid,\n l.category_id,\n l.employee_id,\n sum(l.total) as total,\n to_char(p.date_from, 'YYYY') as year,\n to_char(p.date_from, 'MM') as month,\n to_char(p.date_from, 'YYYY-MM-DD') as day,\n to_char(p.date_to, 'YYYY') as to_year,\n to_char(p.date_to, 'MM') as to_month,\n to_char(p.date_to, 'YYYY-MM-DD') as to_day,\n 1 AS nbr\n from\n hr_payslip as p\n left join hr_payslip_line as l on (p.id=l.slip_id)\n where \n l.employee_id IS NOT NULL\n group by\n p.number,l.name,p.date_from,p.date_to,p.state,p.company_id,p.paid,\n l.employee_id,p.struct_id,l.category_id\n )\n ") + + +payslip_report() \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.pyc_dis b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.pyc_dis new file mode 100644 index 0000000000000..e275c9a0c93f1 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.pyc_dis @@ -0,0 +1,112 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py +import datetime +import time +from openerp.report import report_sxw + +class report_hr_salary_employee_bymonth(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(report_hr_salary_employee_bymonth, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time, + 'get_employee': self.get_employee, + 'get_periods': self.get_periods, + 'get_months_tol': self.get_months_tol, + 'get_total': self.get_total}) + self.context = context + self.mnths = [] + self.mnths_total = [] + self.total = 0.0 + + def get_periods(self, form): + first_year = int(form['start_date'][0:4]) + last_year = int(form['end_date'][0:4]) + first_month = int(form['start_date'][5:7]) + last_month = int(form['end_date'][5:7]) + no_months = (last_year - first_year) * 12 + last_month - first_month + 1 + current_month = first_month + current_year = first_year + mnth_name = [] + for count in range(0, no_months): + m = datetime.date(current_year, current_month, 1).strftime('%b') + mnth_name.append(m) + self.mnths.append(str(current_month) + '-' + str(current_year)) + if current_month == 12: + current_month = 0 + current_year = last_year + current_month = current_month + 1 + + for c in range(0, 12 - no_months): + mnth_name.append('None') + self.mnths.append('None') + + return [mnth_name] + + def get_salary(self, form, emp_id, emp_salary, total_mnths): + category_id = form.get('category_id', []) + category_id = category_id and category_id[0] or False + self.cr.execute("select to_char(date_to,'mm-yyyy') as to_date ,sum(pl.total) from hr_payslip_line as pl left join hr_payslip as p on pl.slip_id = p.id left join hr_employee as emp on emp.id = p.employee_id left join resource_resource as r on r.id = emp.resource_id where p.state = 'done' and p.employee_id = %s and pl.category_id = %s group by r.name, p.date_to,emp.id", (emp_id, category_id)) + sal = self.cr.fetchall() + salary = dict(sal) + total = 0.0 + cnt = 1 + for month in self.mnths: + if month != 'None': + if len(month) != 7: + month = '0' + str(month) + if month in salary and salary[month]: + emp_salary.append(salary[month]) + total += salary[month] + total_mnths[cnt] = total_mnths[cnt] + salary[month] + else: + emp_salary.append(0.0) + else: + emp_salary.append('') + total_mnths[cnt] = '' + cnt = cnt + 1 + + return (emp_salary, total, total_mnths) + + def get_employee(self, form): + emp_salary = [] + salary_list = [] + total_mnths = ['Total', + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0] + emp_obj = self.pool.get('hr.employee') + emp_ids = form.get('employee_ids', []) + employees = emp_obj.browse(self.cr, self.uid, emp_ids, context=self.context) + for emp_id in employees: + emp_salary.append(emp_id.name) + total = 0.0 + emp_salary, total, total_mnths = self.get_salary(form, emp_id.id, emp_salary, total_mnths) + emp_salary.append(total) + salary_list.append(emp_salary) + emp_salary = [] + + self.mnths_total.append(total_mnths) + return salary_list + + def get_months_tol(self): + return self.mnths_total + + def get_total(self): + for item in self.mnths_total: + for count in range(1, len(item)): + if item[count] == '': + continue + self.total += item[count] + + return self.total + + +report_sxw.report_sxw('report.salary.employee.bymonth', 'hr.salary.employee.month', 'l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.rml', parser=report_hr_salary_employee_bymonth, header='internal') \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.pyc_dis b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.pyc_dis new file mode 100644 index 0000000000000..189a4f40bd4ee --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.pyc_dis @@ -0,0 +1,133 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py +import time +import datetime +from openerp.report import report_sxw + +class employees_yearly_salary_report(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(employees_yearly_salary_report, self).__init__(cr, uid, name, context) + self.localcontext.update({'time': time, + 'get_employee': self.get_employee, + 'get_employee_detail': self.get_employee_detail, + 'cal_monthly_amt': self.cal_monthly_amt, + 'get_periods': self.get_periods, + 'get_total': self.get_total, + 'get_allow': self.get_allow, + 'get_deduct': self.get_deduct}) + self.context = context + + def get_periods(self, form): + self.mnths = [] + first_year = int(form['date_from'][0:4]) + last_year = int(form['date_to'][0:4]) + first_month = int(form['date_from'][5:7]) + last_month = int(form['date_to'][5:7]) + no_months = (last_year - first_year) * 12 + last_month - first_month + 1 + current_month = first_month + current_year = first_year + mnth_name = [] + for count in range(0, no_months): + m = datetime.date(current_year, current_month, 1).strftime('%b') + mnth_name.append(m) + self.mnths.append(str(current_month) + '-' + str(current_year)) + if current_month == 12: + current_month = 0 + current_year = last_year + current_month = current_month + 1 + + for c in range(0, 12 - no_months): + mnth_name.append('None') + self.mnths.append('None') + + return [mnth_name] + + def get_employee(self, form): + return self.pool.get('hr.employee').browse(self.cr, self.uid, form.get('employee_ids', []), context=self.context) + + def get_employee_detail(self, form, obj): + self.allow_list = [] + self.deduct_list = [] + self.total = 0.0 + gross = False + net = False + payslip_lines = self.cal_monthly_amt(form, obj.id) + for line in payslip_lines: + for line[0] in line: + if line[0][0] == 'Gross': + gross = line[0] + elif line[0][0] == 'Net': + net = line[0] + elif line[0][13] > 0.0 and line[0][0] != 'Net': + self.total += line[0][len(line[0]) - 1] + self.allow_list.append(line[0]) + elif line[0][13] < 0.0: + self.total += line[0][len(line[0]) - 1] + self.deduct_list.append(line[0]) + + if gross: + self.allow_list.append(gross) + if net: + self.deduct_list.append(net) + return None + + def cal_monthly_amt(self, form, emp_id): + category_obj = self.pool.get('hr.salary.rule.category') + result = [] + res = [] + salaries = {} + self.cr.execute("SELECT rc.code, pl.name, sum(pl.total), to_char(date_to,'mm-yyyy') as to_date FROM hr_payslip_line as pl LEFT JOIN hr_salary_rule_category AS rc on (pl.category_id = rc.id) LEFT JOIN hr_payslip as p on pl.slip_id = p.id LEFT JOIN hr_employee as emp on emp.id = p.employee_id WHERE p.employee_id = %s GROUP BY rc.parent_id, pl.sequence, pl.id, pl.category_id,pl.name,p.date_to,rc.code ORDER BY pl.sequence, rc.parent_id", (emp_id,)) + salary = self.cr.fetchall() + for category in salary: + if category[0] not in salaries: + salaries.setdefault(category[0], {}) + salaries[category[0]].update({category[1]: {category[3]: category[2]}}) + elif category[1] not in salaries[category[0]]: + salaries[category[0]].setdefault(category[1], {}) + salaries[category[0]][category[1]].update({category[3]: category[2]}) + else: + salaries[category[0]][category[1]].update({category[3]: category[2]}) + + category_ids = category_obj.search(self.cr, self.uid, [], context=self.context) + categories = category_obj.read(self.cr, self.uid, category_ids, ['code'], context=self.context) + for code in map(lambda x: x['code'], categories): + if code in salaries: + res = self.salary_list(salaries[code]) + result.append(res) + + return result + + def salary_list(self, salaries): + cat_salary_all = [] + for category_name, amount in salaries.items(): + cat_salary = [] + total = 0.0 + cat_salary.append(category_name) + for mnth in self.mnths: + if mnth != 'None': + if len(mnth) != 7: + mnth = '0' + str(mnth) + if mnth in amount and amount[mnth]: + cat_salary.append(amount[mnth]) + total += amount[mnth] + else: + cat_salary.append(0.0) + else: + cat_salary.append('') + + cat_salary.append(total) + cat_salary_all.append(cat_salary) + + return cat_salary_all + + def get_allow(self): + return self.allow_list + + def get_deduct(self): + return self.deduct_list + + def get_total(self): + return self.total + + +report_sxw.report_sxw('report.salary.detail.byyear', 'yearly.salary.detail', 'hr_payroll/report/report_hr_yearly_salary_detail.rml', parser=employees_yearly_salary_report, header='internal landscape') \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/report_payroll_advice.pyc_dis b/addons/l10n_in_hr_payroll/report/report_payroll_advice.pyc_dis new file mode 100644 index 0000000000000..1e40be2c8da29 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_payroll_advice.pyc_dis @@ -0,0 +1,53 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/report/report_payroll_advice.py +import time +from datetime import datetime +from openerp.report import report_sxw +from openerp.tools import amount_to_text_en + +class payroll_advice_report(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(payroll_advice_report, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time, + 'get_month': self.get_month, + 'convert': self.convert, + 'get_detail': self.get_detail, + 'get_bysal_total': self.get_bysal_total}) + self.context = context + + def get_month(self, input_date): + payslip_pool = self.pool.get('hr.payslip') + res = {'from_name': '', + 'to_name': ''} + slip_ids = payslip_pool.search(self.cr, self.uid, [('date_from', '<=', input_date), ('date_to', '>=', input_date)], context=self.context) + if slip_ids: + slip = payslip_pool.browse(self.cr, self.uid, slip_ids, context=self.context)[0] + from_date = datetime.strptime(slip.date_from, '%Y-%m-%d') + to_date = datetime.strptime(slip.date_to, '%Y-%m-%d') + res['from_name'] = from_date.strftime('%d') + '-' + from_date.strftime('%B') + '-' + from_date.strftime('%Y') + res['to_name'] = to_date.strftime('%d') + '-' + to_date.strftime('%B') + '-' + to_date.strftime('%Y') + return res + + def convert(self, amount, cur): + return amount_to_text_en.amount_to_text(amount, 'en', cur) + + def get_bysal_total(self): + return self.total_bysal + + def get_detail(self, line_ids): + result = [] + self.total_bysal = 0.0 + for l in line_ids: + res = {} + res.update({'name': l.employee_id.name, + 'acc_no': l.name, + 'ifsc_code': l.ifsc_code, + 'bysal': l.bysal, + 'debit_credit': l.debit_credit}) + self.total_bysal += l.bysal + result.append(res) + + return result + + +report_sxw.report_sxw('report.payroll.advice', 'hr.payroll.advice', 'l10n_in_hr_payroll/report/report_payroll_advice.rml', parser=payroll_advice_report, header='external') \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/report_payslip_details.pyc_dis b/addons/l10n_in_hr_payroll/report/report_payslip_details.pyc_dis new file mode 100644 index 0000000000000..23336ddf0576a --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_payslip_details.pyc_dis @@ -0,0 +1,12 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/report/report_payslip_details.py +from openerp.report import report_sxw +from hr_payroll import report + +class payslip_details_report_in(report.report_payslip_details.payslip_details_report): + + def __init__(self, cr, uid, name, context): + super(payslip_details_report_in, self).__init__(cr, uid, name, context) + self.localcontext.update({'get_details_by_rule_category': self.get_details_by_rule_category}) + + +report_sxw.report_sxw('report.paylip.details.in', 'hr.payslip', 'l10n_in_hr_payroll/report/report_payslip_details.rml', parser=payslip_details_report_in) \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/wizard/__init__.pyc_dis b/addons/l10n_in_hr_payroll/wizard/__init__.pyc_dis new file mode 100644 index 0000000000000..4d156e8013a69 --- /dev/null +++ b/addons/l10n_in_hr_payroll/wizard/__init__.pyc_dis @@ -0,0 +1,3 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/wizard/__init__.py +import hr_salary_employee_bymonth +import hr_yearly_salary_detail \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.pyc_dis b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.pyc_dis new file mode 100644 index 0000000000000..372eb0b08054c --- /dev/null +++ b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.pyc_dis @@ -0,0 +1,41 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.py +import time +from openerp.osv import fields, osv + +class hr_salary_employee_bymonth(osv.osv_memory): + _name = 'hr.salary.employee.month' + _description = 'Hr Salary Employee By Month Report' + _columns = {'start_date': fields.date('Start Date', required=True), + 'end_date': fields.date('End Date', required=True), + 'employee_ids': fields.many2many('hr.employee', 'payroll_year_rel', 'payroll_year_id', 'employee_id', 'Employees', required=True), + 'category_id': fields.many2one('hr.salary.rule.category', 'Category', required=True)} + + def _get_default_category(self, cr, uid, context = None): + category_ids = self.pool.get('hr.salary.rule.category').search(cr, uid, [('code', '=', 'NET')], context=context) + return category_ids and category_ids[0] or False + + _defaults = {'start_date': lambda *a: time.strftime('%Y-01-01'), + 'end_date': lambda *a: time.strftime('%Y-%m-%d'), + 'category_id': _get_default_category} + + def print_report(self, cr, uid, ids, context = None): + """ + To get the date and print the report + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param context: A standard dictionary + @return: return report + """ + if context is None: + context = {} + datas = {'ids': context.get('active_ids', [])} + res = self.read(cr, uid, ids, context=context) + res = res and res[0] or {} + datas.update({'form': res}) + return {'type': 'ir.actions.report.xml', + 'report_name': 'salary.employee.bymonth', + 'datas': datas} + + +hr_salary_employee_bymonth() \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.pyc_dis b/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.pyc_dis new file mode 100644 index 0000000000000..24e9eac0c367f --- /dev/null +++ b/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.pyc_dis @@ -0,0 +1,34 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.py +import time +from openerp.osv import fields, osv + +class yearly_salary_detail(osv.osv_memory): + _name = 'yearly.salary.detail' + _description = 'Hr Salary Employee By Category Report' + _columns = {'employee_ids': fields.many2many('hr.employee', 'payroll_emp_rel', 'payroll_id', 'employee_id', 'Employees', required=True), + 'date_from': fields.date('Start Date', required=True), + 'date_to': fields.date('End Date', required=True)} + _defaults = {'date_from': lambda *a: time.strftime('%Y-01-01'), + 'date_to': lambda *a: time.strftime('%Y-%m-%d')} + + def print_report(self, cr, uid, ids, context = None): + """ + To get the date and print the report + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param context: A standard dictionary + @return: return report + """ + if context is None: + context = {} + datas = {'ids': context.get('active_ids', [])} + res = self.read(cr, uid, ids, context=context) + res = res and res[0] or {} + datas.update({'form': res}) + return {'type': 'ir.actions.report.xml', + 'report_name': 'salary.detail.byyear', + 'datas': datas} + + +yearly_salary_detail() \ No newline at end of file diff --git a/addons/l10n_it/__init__.pyc_dis b/addons/l10n_it/__init__.pyc_dis new file mode 100644 index 0000000000000..68a5e1f4be69a --- /dev/null +++ b/addons/l10n_it/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_it/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_it/account_chart.xml b/addons/l10n_it/account_chart.xml index 7d3ef5909ab67..6dd7b539b492f 100644 --- a/addons/l10n_it/account_chart.xml +++ b/addons/l10n_it/account_chart.xml @@ -12,12 +12,5 @@ - - Italy - Generic Chart of Accounts - account.chart.template - default - - - diff --git a/addons/l10n_it/data/account.tax.code.template.csv b/addons/l10n_it/data/account.tax.code.template.csv index 18c00b12777a0..e543594555657 100644 --- a/addons/l10n_it/data/account.tax.code.template.csv +++ b/addons/l10n_it/data/account.tax.code.template.csv @@ -75,21 +75,3 @@ IVC21Idet40,template_impcode_pagata_21det40,IVA a credito 21% detraibile 40% (im IVC21det50,template_ivacode_pagata_21det50,IVA a credito 21% detraibile 50%,template_ivacode_pagata IVC21Ndet50,template_ivacode_pagata_21det50ind,IVA a credito 21% detraibile 50% (indetraibile),template_ivacode_pagata_ind IVC21Idet50,template_impcode_pagata_21det50,IVA a credito 21% detraibile 50% (imponibile),template_impcode_pagata -IVC22,template_ivacode_pagata_22,IVA a credito 22%,template_ivacode_pagata -IVC22I,template_impcode_pagata_22,IVA a credito 22% (imponibile),template_impcode_pagata -IVD22,template_ivacode_riscossa_22,IVA a debito 22%,template_ivacode_riscossa -IVD22I,template_impcode_riscossa_22,IVA a debito 22% (imponibile),template_impcode_riscossa -IVC22ind,template_ivacode_pagata_22ind,IVA a credito 22% indetraibile,template_ivacode_pagata_ind -IVC22Iind,template_impcode_pagata_22ind,IVA a credito 22% indetraibile (imponibile),template_impcode_pagata -IVC22det10,template_ivacode_pagata_22det10,IVA a credito 22% detraibile 10%,template_ivacode_pagata -IVC22Ndet10,template_ivacode_pagata_22det10ind,IVA a credito 22% detraibile 10% (indetraibile),template_ivacode_pagata_ind -IVC22Idet10,template_impcode_pagata_22det10,IVA a credito 22% detraibile 10% (imponibile),template_impcode_pagata -IVC22det15,template_ivacode_pagata_22det15,IVA a credito 22% detraibile 15%,template_ivacode_pagata -IVC22Ndet15,template_ivacode_pagata_22det15ind,IVA a credito 22% detraibile 15% (indetraibile),template_ivacode_pagata_ind -IVC22Idet15,template_impcode_pagata_22det15,IVA a credito 22% detraibile 15% (imponibile),template_impcode_pagata -IVC22det40,template_ivacode_pagata_22det40,IVA a credito 22% detraibile 40%,template_ivacode_pagata -IVC22Ndet40,template_ivacode_pagata_22det40ind,IVA a credito 22% detraibile 40% (indetraibile),template_ivacode_pagata_ind -IVC22Idet40,template_impcode_pagata_22det40,IVA a credito 22% detraibile 40% (imponibile),template_impcode_pagata -IVC22det50,template_ivacode_pagata_22det50,IVA a credito 22% detraibile 50%,template_ivacode_pagata -IVC22Ndet50,template_ivacode_pagata_22det50ind,IVA a credito 22% detraibile 50% (indetraibile),template_ivacode_pagata_ind -IVC22Idet50,template_impcode_pagata_22det50,IVA a credito 22% detraibile 50% (imponibile),template_impcode_pagata diff --git a/addons/l10n_it/data/account.tax.template.csv b/addons/l10n_it/data/account.tax.template.csv index 67daee0eeef20..5654b32e5e9bd 100644 --- a/addons/l10n_it/data/account.tax.template.csv +++ b/addons/l10n_it/data/account.tax.template.csv @@ -1,8 +1,6 @@ id,description,chart_template_id:id,name,sequence,amount,parent_id:id,child_depend,type,account_collected_id:id,account_paid_id:id,type_tax_use,base_code_id:id,tax_code_id:id,ref_base_code_id:id,ref_tax_code_id:id,ref_base_sign,ref_tax_sign,price_include,base_sign,tax_sign -22v,22v,l10n_it_chart_template_generic,Iva al 22% (debito),1,0.22,,False,percent,2601,2601,sale,template_impcode_riscossa_22,template_ivacode_riscossa_22,template_impcode_riscossa_22,template_ivacode_riscossa_22,-1,-1,False,1,1 -22a,22a,l10n_it_chart_template_generic,Iva al 22% (credito),2,0.22,,False,percent,1601,1601,purchase,template_impcode_pagata_22,template_ivacode_pagata_22,template_impcode_pagata_22,template_ivacode_pagata_22,1,1,False,-1,-1 -21v,21v,l10n_it_chart_template_generic,Iva al 21% (debito),3,0.21,,False,percent,2601,2601,sale,template_impcode_riscossa_21,template_ivacode_riscossa_21,template_impcode_riscossa_21,template_ivacode_riscossa_21,-1,-1,False,1,1 -21a,21a,l10n_it_chart_template_generic,Iva al 21% (credito),4,0.21,,False,percent,1601,1601,purchase,template_impcode_pagata_21,template_ivacode_pagata_21,template_impcode_pagata_21,template_ivacode_pagata_21,1,1,False,-1,-1 +21v,21v,l10n_it_chart_template_generic,Iva al 21% (debito),1,0.21,,False,percent,2601,2601,sale,template_impcode_riscossa_21,template_ivacode_riscossa_21,template_impcode_riscossa_21,template_ivacode_riscossa_21,-1,-1,False,1,1 +21a,21a,l10n_it_chart_template_generic,Iva al 21% (credito),2,0.21,,False,percent,1601,1601,purchase,template_impcode_pagata_21,template_ivacode_pagata_21,template_impcode_pagata_21,template_ivacode_pagata_21,1,1,False,-1,-1 20v,20v,l10n_it_chart_template_generic,Iva al 20% (debito),3,0.2,,False,percent,2601,2601,sale,template_impcode_riscossa_20,template_ivacode_riscossa_20,template_impcode_riscossa_20,template_ivacode_riscossa_20,-1,-1,False,1,1 20a,20a,l10n_it_chart_template_generic,Iva al 20% (credito),4,0.2,,False,percent,1601,1601,purchase,template_impcode_pagata_20,template_ivacode_pagata_20,template_impcode_pagata_20,template_ivacode_pagata_20,1,1,False,-1,-1 10v,10v,l10n_it_chart_template_generic,Iva al 10% (debito),5,0.1,,False,percent,2601,2601,sale,template_impcode_riscossa_10,template_ivacode_riscossa_10,template_impcode_riscossa_10,template_ivacode_riscossa_10,-1,-1,False,1,1 @@ -27,8 +25,8 @@ id,description,chart_template_id:id,name,sequence,amount,parent_id:id,child_depe 20I5,20I5,l10n_it_chart_template_generic,IVA al 20% detraibile al 50%,14,0.2,,True,percent,,,purchase,template_impcode_pagata_20det50,,template_impcode_pagata_20det50,,1,1,False,-1,-1 20I5b,20I5b,l10n_it_chart_template_generic,IVA al 20% detraibile al 50% (D),200,0,20I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_20det50,,template_ivacode_pagata_20det50,1,1,False,-1,-1 20I5a,20I5a,l10n_it_chart_template_generic,IVA al 20% detraibile al 50% (I),100,0.5,20I5,False,percent,,,purchase,,template_ivacode_pagata_20det50ind,,template_ivacode_pagata_20det50ind,1,1,False,-1,-1 -2v,2v,l10n_it_chart_template_generic,Iva 2% (debito),15,0.02,,False,percent,2601,2601,sale,template_impcode_riscossa_2,template_ivacode_riscossa_2,template_impcode_riscossa_2,template_ivacode_riscossa_2,-1,-1,False,1,1 -2a,2a,l10n_it_chart_template_generic,Iva 2% (credito),16,0.02,,False,percent,1601,1601,purchase,template_impcode_pagata_2,template_ivacode_pagata_2,template_impcode_pagata_2,template_ivacode_pagata_2,1,1,False,-1,-1 +22v,22v,l10n_it_chart_template_generic,Iva 2% (debito),15,0.02,,False,percent,2601,2601,sale,template_impcode_riscossa_2,template_ivacode_riscossa_2,template_impcode_riscossa_2,template_ivacode_riscossa_2,-1,-1,False,1,1 +22a,22a,l10n_it_chart_template_generic,Iva 2% (credito),16,0.02,,False,percent,1601,1601,purchase,template_impcode_pagata_2,template_ivacode_pagata_2,template_impcode_pagata_2,template_ivacode_pagata_2,1,1,False,-1,-1 4v,4v,l10n_it_chart_template_generic,Iva 4% (debito),17,0.04,,False,percent,2601,2601,sale,template_impcode_riscossa_4,template_ivacode_riscossa_4,template_impcode_riscossa_4,template_ivacode_riscossa_4,-1,-1,False,1,1 4a,4a,l10n_it_chart_template_generic,Iva 4% (credito),18,0.04,,False,percent,1601,1601,purchase,template_impcode_pagata_4,template_ivacode_pagata_4,template_impcode_pagata_4,template_ivacode_pagata_4,1,1,False,-1,-1 4AO,4AO,l10n_it_chart_template_generic,Iva al 4% indetraibile,19,0.04,,True,percent,,,purchase,template_impcode_pagata_4ind,,template_impcode_pagata_4ind,,1,1,False,-1,-1 @@ -44,12 +42,11 @@ id,description,chart_template_id:id,name,sequence,amount,parent_id:id,child_depe 00a,00a,l10n_it_chart_template_generic,Fuori Campo IVA (credito),23,0,,False,percent,1601,1601,purchase,template_impcode_pagata_0,template_ivacode_pagata_0,template_impcode_pagata_0,template_ivacode_pagata_0,1,1,False,-1,-1 00art15v,00art15v,l10n_it_chart_template_generic,Imponibile Escluso Art.15 (debito),22,0,,False,percent,2601,2601,sale,template_impcode_riscossa_art15,template_ivacode_riscossa_art15,template_impcode_riscossa_art15,template_ivacode_riscossa_art15,-1,-1,False,1,1 00art15a,00art15a,l10n_it_chart_template_generic,Imponibile Escluso Art.15 (credito),23,0,,False,percent,1601,1601,purchase,template_impcode_pagata_art15,template_ivacode_pagata_art15,template_impcode_pagata_art15,template_ivacode_pagata_art15,1,1,False,-1,-1 -22v INC,22v INC,l10n_it_chart_template_generic,Iva al 22% (debito) INC,24,0.22,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_22,l10n_it.template_ivacode_riscossa_22,l10n_it.template_impcode_riscossa_22,l10n_it.template_ivacode_riscossa_22,-1,-1,True,1,1 -21v INC,21v INC,l10n_it_chart_template_generic,Iva al 21% (debito) INC,25,0.21,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_21,l10n_it.template_ivacode_riscossa_21,l10n_it.template_impcode_riscossa_21,l10n_it.template_ivacode_riscossa_21,-1,-1,True,1,1 +21v INC,21v INC,l10n_it_chart_template_generic,Iva al 21% (debito) INC,24,0.21,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_21,l10n_it.template_ivacode_riscossa_21,l10n_it.template_impcode_riscossa_21,l10n_it.template_ivacode_riscossa_21,-1,-1,True,1,1 20v INC,20v INC,l10n_it_chart_template_generic,Iva al 20% (debito) INC,25,0.2,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_20,l10n_it.template_ivacode_riscossa_20,l10n_it.template_impcode_riscossa_20,l10n_it.template_ivacode_riscossa_20,-1,-1,True,1,1 10v INC,10v INC,l10n_it_chart_template_generic,Iva al 10% (debito) INC,26,0.1,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_10,l10n_it.template_ivacode_riscossa_10,l10n_it.template_impcode_riscossa_10,l10n_it.template_ivacode_riscossa_10,-1,-1,True,1,1 12v INC,12v INC,l10n_it_chart_template_generic,Iva 12% (debito) INC,27,0.12,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_12,l10n_it.template_ivacode_riscossa_12,l10n_it.template_impcode_riscossa_12,l10n_it.template_ivacode_riscossa_12,-1,-1,True,1,1 -2v INC,2v INC,l10n_it_chart_template_generic,Iva 2% (debito) INC,28,0.02,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_2,l10n_it.template_ivacode_riscossa_2,l10n_it.template_impcode_riscossa_2,l10n_it.template_ivacode_riscossa_2,-1,-1,True,1,1 +22v INC,22v INC,l10n_it_chart_template_generic,Iva 2% (debito) INC,28,0.02,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_2,l10n_it.template_ivacode_riscossa_2,l10n_it.template_impcode_riscossa_2,l10n_it.template_ivacode_riscossa_2,-1,-1,True,1,1 4v INC,4v INC,l10n_it_chart_template_generic,Iva 4% (debito) INC,29,0.04,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_4,l10n_it.template_ivacode_riscossa_4,l10n_it.template_impcode_riscossa_4,l10n_it.template_ivacode_riscossa_4,-1,-1,True,1,1 00v INC,00v INC,l10n_it_chart_template_generic,Fuori Campo IVA (debito) INC,30,0,,False,percent,l10n_it.2601,l10n_it.2601,sale,l10n_it.template_impcode_riscossa_0,l10n_it.template_ivacode_riscossa_0,l10n_it.template_impcode_riscossa_0,l10n_it.template_ivacode_riscossa_0,-1,-1,True,1,1 2110,2110,l10n_it_chart_template_generic,Iva al 21% detraibile 10%,31,0.21,,True,percent,,,purchase,template_impcode_pagata_21det10,,template_impcode_pagata_21det10,,1,1,False,-1,-1 @@ -67,18 +64,3 @@ id,description,chart_template_id:id,name,sequence,amount,parent_id:id,child_depe 21I5,21I5,l10n_it_chart_template_generic,IVA al 21% detraibile al 50%,35,0.21,,True,percent,,,purchase,template_impcode_pagata_21det50,,template_impcode_pagata_21det50,,1,1,False,-1,-1 21I5b,21I5b,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (D),200,0,21I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_21det50,,template_ivacode_pagata_21det50,1,1,False,-1,-1 21I5a,21I5a,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (I),100,0.5,21I5,False,percent,,,purchase,,template_ivacode_pagata_21det50ind,,template_ivacode_pagata_21det50ind,1,1,False,-1,-1 -2210,2210,l10n_it_chart_template_generic,Iva al 22% detraibile 10%,31,0.22,,True,percent,,,purchase,template_impcode_pagata_22det10,,template_impcode_pagata_22det10,,1,1,False,-1,-1 -2210b,2210b,l10n_it_chart_template_generic,Iva al 22% detraibile 10% (D),200,0,2210,False,balance,1601,1601,purchase,,template_ivacode_pagata_22det10,,template_ivacode_pagata_22det10,1,1,False,-1,-1 -2210a,2210a,l10n_it_chart_template_generic,Iva al 22% detraibile 10% (I),100,0.9,2210,False,percent,,,purchase,,template_ivacode_pagata_22det10ind,,template_ivacode_pagata_22det10ind,1,1,False,-1,-1 -2215,2215,l10n_it_chart_template_generic,Iva al 22% detraibile 15%,32,0.22,,True,percent,,,purchase,template_impcode_pagata_22det15,,template_impcode_pagata_22det15,,1,1,False,-1,-1 -2215b,2215b,l10n_it_chart_template_generic,Iva al 22% detraibile 15% (D),200,0,2215,False,balance,1601,1601,purchase,,template_ivacode_pagata_22det15,,template_ivacode_pagata_22det15,1,1,False,-1,-1 -2215a,2215a,l10n_it_chart_template_generic,Iva al 22% detraibile 15% (I),100,0.85,2215,False,percent,,,purchase,,template_ivacode_pagata_22det15ind,,template_ivacode_pagata_22det15ind,1,1,False,-1,-1 -2240,2240,l10n_it_chart_template_generic,Iva al 22% detraibile 40%,33,0.22,,True,percent,,,purchase,template_impcode_pagata_22det40,,template_impcode_pagata_22det40,,1,1,False,-1,-1 -2240b,2240b,l10n_it_chart_template_generic,Iva al 22% detraibile 40% (D),200,0,2240,False,balance,1601,1601,purchase,,template_ivacode_pagata_22det40,,template_ivacode_pagata_22det40,1,1,False,-1,-1 -2240a,2240a,l10n_it_chart_template_generic,Iva al 22% detraibile 40% (I),100,0.6,2240,False,percent,,,purchase,,template_ivacode_pagata_22det40ind,,template_ivacode_pagata_22det40ind,1,1,False,-1,-1 -22AO,22AO,l10n_it_chart_template_generic,Iva al 22% indetraibile,34,0.22,,True,percent,,,purchase,template_impcode_pagata_22ind,,template_impcode_pagata_22ind,,1,1,False,-1,-1 -22AOb,22AOb,l10n_it_chart_template_generic,Iva al 22% indetraibile (D),200,0,22AO,False,balance,1601,1601,purchase,,template_ivacode_pagata_22ind,,template_ivacode_pagata_22ind,1,1,False,-1,-1 -22AOa,22AOa,l10n_it_chart_template_generic,Iva al 22% indetraibile (I),100,1,22AO,False,percent,,,purchase,,template_ivacode_pagata_22ind,,template_ivacode_pagata_22ind,1,1,False,-1,-1 -22I5,22I5,l10n_it_chart_template_generic,IVA al 22% detraibile al 50%,35,0.22,,True,percent,,,purchase,template_impcode_pagata_22det50,,template_impcode_pagata_22det50,,1,1,False,-1,-1 -22I5b,22I5b,l10n_it_chart_template_generic,IVA al 22% detraibile al 50% (D),200,0,22I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_22det50,,template_ivacode_pagata_22det50,1,1,False,-1,-1 -22I5a,22I5a,l10n_it_chart_template_generic,IVA al 22% detraibile al 50% (I),100,0.5,22I5,False,percent,,,purchase,,template_ivacode_pagata_22det50ind,,template_ivacode_pagata_22det50ind,1,1,False,-1,-1 diff --git a/addons/l10n_it/images/config_chart_l10n_it.jpeg b/addons/l10n_it/images/config_chart_l10n_it.jpeg new file mode 100644 index 0000000000000..8d2f77d6e2260 Binary files /dev/null and b/addons/l10n_it/images/config_chart_l10n_it.jpeg differ diff --git a/addons/l10n_it/images/l10n_it_chart.jpeg b/addons/l10n_it/images/l10n_it_chart.jpeg new file mode 100644 index 0000000000000..598aeac78d8cc Binary files /dev/null and b/addons/l10n_it/images/l10n_it_chart.jpeg differ diff --git a/addons/l10n_lu/__init__.pyc_dis b/addons/l10n_lu/__init__.pyc_dis new file mode 100644 index 0000000000000..1e410aa862de0 --- /dev/null +++ b/addons/l10n_lu/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_lu/__init__.py +import wizard \ No newline at end of file diff --git a/addons/l10n_lu/__openerp__.py b/addons/l10n_lu/__openerp__.py index c49f0ca8410dc..46d2312bb02ac 100644 --- a/addons/l10n_lu/__openerp__.py +++ b/addons/l10n_lu/__openerp__.py @@ -41,7 +41,6 @@ 'data': [ # basic accounting data 'account_financial_report.xml', - 'account_financial_report_abr.xml', 'account.account.type-2011.csv', 'account.account.template-2011.csv', 'account.tax.code.template-2011.csv', diff --git a/addons/l10n_lu/account.account.template-2011.csv b/addons/l10n_lu/account.account.template-2011.csv index efd909b675958..4124da146e7f1 100644 --- a/addons/l10n_lu/account.account.template-2011.csv +++ b/addons/l10n_lu/account.account.template-2011.csv @@ -1,1159 +1,1159 @@ -id,code,name,parent_id/id,type,user_type/id,reconcile,financial_report_ids/id -lu_2011_account_0,0,Plan de compte Luxembourgeois - Loi de Juin 2009 (THAMINI & ADN & ACSONE),,view,account.data_account_type_view,f, -lu_2011_account_bilan,bilan,TOTAL CLASSES 1 A 5,lu_2011_account_0,view,account.data_account_type_view,f, -lu_2011_account_1,1,"CLASSE 1 - COMPTES DE CAPITAUX, DE PROVISIONS ET DE DETTES FINANCIERES",lu_2011_account_bilan,view,account_type_2011_1_capital,f, -lu_2011_account_10,10,Capital ou dotation des succursales et comptes de l'exploitant,lu_2011_account_1,view,account_type_2011_1_capital,f, -lu_2011_account_101,101,Capital souscrit (Sociétés de capitaux - Montant total),lu_2011_account_10,other,account_type_2011_1_capital,f,"account_financial_report_68,account_financial_report_abr_68" -lu_2011_account_102,102,Capital souscrit non appelé (Sociétés de capitaux),lu_2011_account_10,other,account_type_2011_1_capital,f,"account_financial_report_17,account_financial_report_abr_17" -lu_2011_account_103,103,Capital souscrit appelé et non versé (Sociétés de capitaux),lu_2011_account_10,other,account_type_2011_1_capital,f,"account_financial_report_18,account_financial_report_abr_18" -lu_2011_account_104,104,Capital des entreprises commerçants personnes physiques et des sociétés de personnes,lu_2011_account_10,view,account_type_2011_1_capital,f,"account_financial_report_68,account_financial_report_abr_68" -lu_2011_account_1041,1041,Commerçants personnes physiques,lu_2011_account_104,other,account_type_2011_1_capital,f, -lu_2011_account_1042,1042,Sociétés de personnes,lu_2011_account_104,other,account_type_2011_1_capital,f, -lu_2011_account_105,105,Dotation des succursales,lu_2011_account_10,other,account_type_2011_1_capital,f,"account_financial_report_68,account_financial_report_abr_68" -lu_2011_account_106,106,Comptes de l'exploitant ou des coexploitants,lu_2011_account_10,view,account_type_2011_1_capital,f,"account_financial_report_68,account_financial_report_abr_68" -lu_2011_account_1061,1061,Prélèvements privés de l'exploitant ou des coexploitants,lu_2011_account_106,view,account_type_2011_1_capital,f, -lu_2011_account_10611,10611,Prélèvements en numéraire (train de vie),lu_2011_account_1061,other,account_type_2011_1_capital,f, -lu_2011_account_10612,10612,"Prélèvements en nature de marchandises, de produits finis et services (au prix de revient)",lu_2011_account_1061,other,account_type_2011_1_capital,f, -lu_2011_account_10613,10613,Part personnelle des frais de maladie,lu_2011_account_1061,other,account_type_2011_1_capital,f, -lu_2011_account_10614,10614,Primes d'assurances privées,lu_2011_account_1061,view,account_type_2011_1_capital,f, -lu_2011_account_106141,106141,Vie,lu_2011_account_10614,other,account_type_2011_1_capital,f, -lu_2011_account_106142,106142,Accident,lu_2011_account_10614,other,account_type_2011_1_capital,f, -lu_2011_account_106143,106143,Incendie,lu_2011_account_10614,other,account_type_2011_1_capital,f, -lu_2011_account_106144,106144,Responsabilité civile,lu_2011_account_10614,other,account_type_2011_1_capital,f, -lu_2011_account_106145,106145,Multirisques,lu_2011_account_10614,other,account_type_2011_1_capital,f, -lu_2011_account_106148,106148,Autres primes d'assurances privées,lu_2011_account_10614,other,account_type_2011_1_capital,f, -lu_2011_account_10615,10615,Cotisations,lu_2011_account_1061,view,account_type_2011_1_capital,f, -lu_2011_account_106151,106151,Assurances sociales (assurance dépendance),lu_2011_account_10615,other,account_type_2011_1_capital,f, -lu_2011_account_106152,106152,Allocations familiales,lu_2011_account_10615,other,account_type_2011_1_capital,f, -lu_2011_account_106153,106153,Cotisations pour mutuelles,lu_2011_account_10615,other,account_type_2011_1_capital,f, -lu_2011_account_106154,106154,"Caisse de décès, médico-chirurgicale, Prestaplus",lu_2011_account_10615,other,account_type_2011_1_capital,f, -lu_2011_account_106158,106158,Autres cotisations,lu_2011_account_10615,other,account_type_2011_1_capital,f, -lu_2011_account_10616,10616,Prélèvements en nature (quote-part privée dans les frais généraux),lu_2011_account_1061,view,account_type_2011_1_capital,f, -lu_2011_account_106161,106161,Salaires,lu_2011_account_10616,other,account_type_2011_1_capital,f, -lu_2011_account_106162,106162,Loyer,lu_2011_account_10616,other,account_type_2011_1_capital,f, -lu_2011_account_106163,106163,"Chauffage, gaz, électricité",lu_2011_account_10616,other,account_type_2011_1_capital,f, -lu_2011_account_106164,106164,Eau,lu_2011_account_10616,other,account_type_2011_1_capital,f, -lu_2011_account_106165,106165,Téléphone,lu_2011_account_10616,other,account_type_2011_1_capital,f, -lu_2011_account_106166,106166,Voiture,lu_2011_account_10616,other,account_type_2011_1_capital,f, -lu_2011_account_106168,106168,Autres prélèvements en nature,lu_2011_account_10616,other,account_type_2011_1_capital,f, -lu_2011_account_10617,10617,Acquisitions,lu_2011_account_1061,view,account_type_2011_1_capital,f, -lu_2011_account_106171,106171,Mobilier privé,lu_2011_account_10617,other,account_type_2011_1_capital,f, -lu_2011_account_106172,106172,Voiture privée,lu_2011_account_10617,other,account_type_2011_1_capital,f, -lu_2011_account_106173,106173,Titres privés,lu_2011_account_10617,other,account_type_2011_1_capital,f, -lu_2011_account_106174,106174,Immeubles privés,lu_2011_account_10617,other,account_type_2011_1_capital,f, -lu_2011_account_106178,106178,Autres acquisitions,lu_2011_account_10617,other,account_type_2011_1_capital,f, -lu_2011_account_10618,10618,Impôts,lu_2011_account_1061,view,account_type_2011_1_capital,f, -lu_2011_account_106181,106181,Impôt sur le revenu payé,lu_2011_account_10618,other,account_type_2011_1_capital,f, -lu_2011_account_106182,106182,Impôt sur la fortune payé,lu_2011_account_10618,other,account_type_2011_1_capital,f, -lu_2011_account_106183,106183,Impôt commercial - arriérés payés,lu_2011_account_10618,other,account_type_2011_1_capital,f, -lu_2011_account_106188,106188,Autres impôts,lu_2011_account_10618,other,account_type_2011_1_capital,f, -lu_2011_account_10619,10619,Prélèvements privés particuliers,lu_2011_account_1061,view,account_type_2011_1_capital,f, -lu_2011_account_106191,106191,Réparations aux immeubles privés,lu_2011_account_10619,other,account_type_2011_1_capital,f, -lu_2011_account_106192,106192,Placements sur comptes financiers privés,lu_2011_account_10619,other,account_type_2011_1_capital,f, -lu_2011_account_106193,106193,Remboursements de dettes privées,lu_2011_account_10619,other,account_type_2011_1_capital,f, -lu_2011_account_106194,106194,Dons et dotations aux enfants,lu_2011_account_10619,other,account_type_2011_1_capital,f, -lu_2011_account_106195,106195,Droits de succession et droits de mutation par décès,lu_2011_account_10619,other,account_type_2011_1_capital,f, -lu_2011_account_106198,106198,Autres prélèvements privés particuliers,lu_2011_account_10619,other,account_type_2011_1_capital,f, -lu_2011_account_1062,1062,Suppléments d'apports privés de l'exploitant ou des coexploitants,lu_2011_account_106,view,account_type_2011_1_capital,f, -lu_2011_account_10621,10621,Héritage ou donation,lu_2011_account_1062,other,account_type_2011_1_capital,f, -lu_2011_account_10622,10622,Avoirs privés,lu_2011_account_1062,other,account_type_2011_1_capital,f, -lu_2011_account_10623,10623,Emprunts privés,lu_2011_account_1062,other,account_type_2011_1_capital,f, -lu_2011_account_10624,10624,Cessions,lu_2011_account_1062,view,account_type_2011_1_capital,f, -lu_2011_account_106241,106241,Mobilier privé,lu_2011_account_10624,other,account_type_2011_1_capital,f, -lu_2011_account_106242,106242,Voiture privée,lu_2011_account_10624,other,account_type_2011_1_capital,f, -lu_2011_account_106243,106243,Titres privés,lu_2011_account_10624,other,account_type_2011_1_capital,f, -lu_2011_account_106244,106244,Immeubles privés,lu_2011_account_10624,other,account_type_2011_1_capital,f, -lu_2011_account_106248,106248,Autres cessions,lu_2011_account_10624,other,account_type_2011_1_capital,f, -lu_2011_account_10625,10625,Loyers encaissés,lu_2011_account_1062,other,account_type_2011_1_capital,f, -lu_2011_account_10626,10626,Salaires ou rentes touchés,lu_2011_account_1062,other,account_type_2011_1_capital,f, -lu_2011_account_10627,10627,Allocations familiales reçues,lu_2011_account_1062,other,account_type_2011_1_capital,f, -lu_2011_account_10628,10628,Remboursements d'impôts,lu_2011_account_1062,view,account_type_2011_1_capital,f, -lu_2011_account_106281,106281,Impôt sur le revenu,lu_2011_account_10628,other,account_type_2011_1_capital,f, -lu_2011_account_106283,106283,Impôt sur la fortune,lu_2011_account_10628,other,account_type_2011_1_capital,f, -lu_2011_account_106284,106284,Impôt commercial,lu_2011_account_10628,other,account_type_2011_1_capital,f, -lu_2011_account_106288,106288,Autres remboursements d'impôts,lu_2011_account_10628,other,account_type_2011_1_capital,f, -lu_2011_account_10629,10629,Quote-part professionnelle de frais privés,lu_2011_account_1062,other,account_type_2011_1_capital,f, -lu_2011_account_11,11,Primes d'émission et primes assimilées,lu_2011_account_1,view,account_type_2011_1_capital,f,"account_financial_report_69,account_financial_report_abr_69" -lu_2011_account_111,111,Primes d'émission,lu_2011_account_11,other,account_type_2011_1_capital,f, -lu_2011_account_112,112,Primes de fusion,lu_2011_account_11,other,account_type_2011_1_capital,f, -lu_2011_account_113,113,Primes d'apport,lu_2011_account_11,other,account_type_2011_1_capital,f, -lu_2011_account_114,114,Primes de conversion d'obligations en actions,lu_2011_account_11,other,account_type_2011_1_capital,f, -lu_2011_account_115,115,"Apport en capitaux propres non rémunéré par des titres (""Capital contribution"")",lu_2011_account_11,other,account_type_2011_1_capital,f, -lu_2011_account_12,12,Réserves de réévaluation,lu_2011_account_1,view,account_type_2011_1_capital,f,"account_financial_report_70,account_financial_report_abr_70" -lu_2011_account_121,121,Réserves de réévaluation en application de la juste valeur,lu_2011_account_12,other,account_type_2011_1_capital,f, -lu_2011_account_122,122,Réserves de mise en équivalence (Participations valorisées suivant l'art. 58),lu_2011_account_12,other,account_type_2011_1_capital,f, -lu_2011_account_123,123,Plus-values sur écarts de conversion immunisées,lu_2011_account_12,other,account_type_2011_1_capital,f, -lu_2011_account_128,128,Autres réserves de réévaluation,lu_2011_account_12,other,account_type_2011_1_capital,f, -lu_2011_account_13,13,Réserves,lu_2011_account_1,view,account_type_2011_1_capital,f, -lu_2011_account_131,131,Réserve légale,lu_2011_account_13,other,account_type_2011_1_capital,f,"account_financial_report_72,account_financial_report_abr_71" -lu_2011_account_132,132,Réserve pour actions propres ou parts propres,lu_2011_account_13,other,account_type_2011_1_capital,f,"account_financial_report_73,account_financial_report_abr_71" -lu_2011_account_133,133,Réserves statutaires,lu_2011_account_13,other,account_type_2011_1_capital,f,"account_financial_report_74,account_financial_report_abr_71" -lu_2011_account_138,138,Autres réserves,lu_2011_account_13,view,account_type_2011_1_capital,f,"account_financial_report_75,account_financial_report_abr_71" -lu_2011_account_1381,1381,Réserve pour l'impôt sur la fortune,lu_2011_account_138,other,account_type_2011_1_capital,f, -lu_2011_account_1382,1382,Autres réserves indisponibles,lu_2011_account_138,other,account_type_2011_1_capital,f, -lu_2011_account_1383,1383,Autres réserves disponibles,lu_2011_account_138,other,account_type_2011_1_capital,f, -lu_2011_account_14,14,Résultats,lu_2011_account_1,view,account_type_2011_1_capital,f, -lu_2011_account_141,141,Résultats reportés,lu_2011_account_14,other,account_type_2011_1_capital,f,"account_financial_report_76,account_financial_report_abr_76" -lu_2011_account_142,142,Résultat de l'exercice,lu_2011_account_14,other,account_type_2011_1_capital,f,"account_financial_report_77,account_financial_report_abr_77" -lu_2011_account_15,15,Acomptes sur dividendes,lu_2011_account_1,other,account_type_2011_1_capital,f,"account_financial_report_78,account_financial_report_abr_78" -lu_2011_account_16,16,Subventions d'investissement en capital,lu_2011_account_1,view,account_type_2011_1_capital,f,"account_financial_report_79,account_financial_report_abr_79" -lu_2011_account_161,161,Terrains et constructions,lu_2011_account_16,other,account_type_2011_1_capital,f, -lu_2011_account_162,162,Installations techniques et machines,lu_2011_account_16,other,account_type_2011_1_capital,f, -lu_2011_account_163,163,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_16,other,account_type_2011_1_capital,f, -lu_2011_account_168,168,Autres subventions d'investissement en capital,lu_2011_account_16,other,account_type_2011_1_capital,f, -lu_2011_account_17,17,Plus-values immunisées,lu_2011_account_1,view,account_type_2011_1_capital,f,"account_financial_report_80,account_financial_report_abr_80" -lu_2011_account_171,171,Plus-values immunisées à réinvestir,lu_2011_account_17,other,account_type_2011_1_capital,f, -lu_2011_account_172,172,Plus-values immunisées réinvesties,lu_2011_account_17,other,account_type_2011_1_capital,f, -lu_2011_account_18,18,Provisions,lu_2011_account_1,view,account_type_2011_1_provision,f, -lu_2011_account_181,181,Provisions pour pensions et obligations similaires,lu_2011_account_18,other,account_type_2011_1_provision,f,"account_financial_report_83,account_financial_report_abr_82" -lu_2011_account_182,182,Provisions pour impôts,lu_2011_account_18,view,account_type_2011_1_provision,f,"account_financial_report_84,account_financial_report_abr_82" -lu_2011_account_1821,1821,Provisions pour impôt sur le revenu des collectivités,lu_2011_account_182,other,account_type_2011_1_provision,f, -lu_2011_account_1822,1822,Provisions pour impôt commercial,lu_2011_account_182,other,account_type_2011_1_provision,f, -lu_2011_account_1823,1823,Provisions pour impôt sur la fortune,lu_2011_account_182,other,account_type_2011_1_provision,f, -lu_2011_account_1828,1828,Autres provisions pour impôts,lu_2011_account_182,other,account_type_2011_1_provision,f, -lu_2011_account_183,183,Provisions pour impôts différés,lu_2011_account_18,other,account_type_2011_1_provision,f,"account_financial_report_84,account_financial_report_abr_82" -lu_2011_account_188,188,Autres provisions,lu_2011_account_18,view,account_type_2011_1_provision,f,"account_financial_report_85,account_financial_report_abr_82" -lu_2011_account_1881,1881,Provisions d'exploitation,lu_2011_account_188,other,account_type_2011_1_provision,f, -lu_2011_account_1882,1882,Provisions financières,lu_2011_account_188,other,account_type_2011_1_provision,f, -lu_2011_account_1883,1883,Provisions exceptionnelles,lu_2011_account_188,other,account_type_2011_1_provision,f, -lu_2011_account_19,19,Dettes financières et dettes assimilées,lu_2011_account_1,view,account_type_2011_1_provision,f, -lu_2011_account_191,191,Dettes subordonnées,lu_2011_account_19,view,account_type_2011_1_provision,f,"account_financial_report_81,account_financial_report_abr_81" -lu_2011_account_1911,1911,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_191,view,account_type_2011_1_provision,f, -lu_2011_account_19111,19111,Montant principal,lu_2011_account_1911,other,account_type_2011_1_provision,f, -lu_2011_account_19112,19112,Intérêts courus,lu_2011_account_1911,other,account_type_2011_1_provision,f, -lu_2011_account_1912,1912,dont la durée résiduelle est supérieure à un an,lu_2011_account_191,view,account_type_2011_1_provision,f, -lu_2011_account_19121,19121,Montant principal,lu_2011_account_1912,other,account_type_2011_1_provision,f, -lu_2011_account_19122,19122,Intérêts courus,lu_2011_account_1912,other,account_type_2011_1_provision,f, -lu_2011_account_192,192,Emprunts obligataires convertibles,lu_2011_account_19,view,account_type_2011_1_provision,f, -lu_2011_account_1921,1921,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_192,view,account_type_2011_1_provision,f,"account_financial_report_89,account_financial_report_abr_89" -lu_2011_account_19211,19211,Montant principal,lu_2011_account_1921,other,account_type_2011_1_provision,f, -lu_2011_account_19212,19212,Intérêts courus,lu_2011_account_1921,other,account_type_2011_1_provision,f, -lu_2011_account_1922,1922,dont la durée résiduelle est supérieure à un an,lu_2011_account_192,view,account_type_2011_1_provision,f,"account_financial_report_90,account_financial_report_abr_90" -lu_2011_account_19221,19221,Montant principal,lu_2011_account_1922,other,account_type_2011_1_provision,f, -lu_2011_account_19222,19222,Intérêts courus,lu_2011_account_1922,other,account_type_2011_1_provision,f, -lu_2011_account_193,193,Emprunts obligataires non convertibles,lu_2011_account_19,view,account_type_2011_1_provision,f, -lu_2011_account_1931,1931,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_193,view,account_type_2011_1_provision,f,"account_financial_report_92,account_financial_report_abr_89" -lu_2011_account_19311,19311,Montant principal,lu_2011_account_1931,other,account_type_2011_1_provision,f, -lu_2011_account_19312,19312,Intérêts courus,lu_2011_account_1931,other,account_type_2011_1_provision,f, -lu_2011_account_1932,1932,dont la durée résiduelle est supérieure à un an,lu_2011_account_193,view,account_type_2011_1_provision,f,"account_financial_report_93,account_financial_report_abr_90" -lu_2011_account_19321,19321,Montant principal,lu_2011_account_1932,other,account_type_2011_1_provision,f, -lu_2011_account_19322,19322,Intérêts courus,lu_2011_account_1932,other,account_type_2011_1_provision,f, -lu_2011_account_194,194,Dettes envers des établissements de crédit,lu_2011_account_19,view,account_type_2011_1_provision,f, -lu_2011_account_1941,1941,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_194,view,account_type_2011_1_provision,f,"account_financial_report_95,account_financial_report_abr_89" -lu_2011_account_19411,19411,Montant principal,lu_2011_account_1941,other,account_type_2011_1_provision,f, -lu_2011_account_19412,19412,Intérêts courus,lu_2011_account_1941,other,account_type_2011_1_provision,f, -lu_2011_account_1942,1942,dont la durée résiduelle est supérieure à un an,lu_2011_account_194,view,account_type_2011_1_provision,f,"account_financial_report_96,account_financial_report_abr_90" -lu_2011_account_19421,19421,Montant principal,lu_2011_account_1942,other,account_type_2011_1_provision,f, -lu_2011_account_19422,19422,Intérêts courus,lu_2011_account_1942,other,account_type_2011_1_provision,f, -lu_2011_account_195,195,Dettes de leasing financier,lu_2011_account_19,view,account_type_2011_1_provision,f, -lu_2011_account_1951,1951,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_195,other,account_type_2011_1_provision,f,"account_financial_report_95,account_financial_report_abr_89" -lu_2011_account_1952,1952,dont la durée résiduelle est supérieure à un an,lu_2011_account_195,other,account_type_2011_1_provision,f,"account_financial_report_96,account_financial_report_abr_90" -lu_2011_account_198,198,Autres emprunts et dettes assimilées,lu_2011_account_19,view,account_type_2011_1_provision,f, -lu_2011_account_1981,1981,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_198,view,account_type_2011_1_provision,f,"account_financial_report_116,account_financial_report_abr_89" -lu_2011_account_19811,19811,Autres emprunts,lu_2011_account_1981,other,account_type_2011_1_provision,f, -lu_2011_account_19812,19812,Rentes viagères capitalisées,lu_2011_account_1981,other,account_type_2011_1_provision,f, -lu_2011_account_19813,19813,Autres dettes assimilées,lu_2011_account_1981,other,account_type_2011_1_provision,f, -lu_2011_account_19814,19814,Intérêts courus sur autres emprunts et dettes assimilées,lu_2011_account_1981,other,account_type_2011_1_provision,f, -lu_2011_account_1982,1982,dont la durée résiduelle est supérieure à un an,lu_2011_account_198,view,account_type_2011_1_provision,f,"account_financial_report_117,account_financial_report_abr_90" -lu_2011_account_19821,19821,Autres emprunts,lu_2011_account_1982,other,account_type_2011_1_provision,f, -lu_2011_account_19822,19822,Rentes viagères capitalisées,lu_2011_account_1982,other,account_type_2011_1_provision,f, -lu_2011_account_19823,19823,Autres dettes assimilées,lu_2011_account_1982,other,account_type_2011_1_provision,f, -lu_2011_account_19824,19824,Intérêts courus sur autres emprunts et dettes assimilées,lu_2011_account_1982,other,account_type_2011_1_provision,f, -lu_2011_account_2,2,CLASSE 2 - COMPTES DE FRAIS D’ETABLISSEMENT ET D’ACTIFS IMMOBILISES,lu_2011_account_bilan,view,account.data_account_type_view,f, -lu_2011_account_20,20,Frais d'établissement et frais assimilés,lu_2011_account_2,view,account_type_2011_2_immo,f,"account_financial_report_19,account_financial_report_abr_19" -lu_2011_account_201,201,Frais de constitution,lu_2011_account_20,other,account_type_2011_2_immo,f, -lu_2011_account_202,202,Frais de premier établissement,lu_2011_account_20,view,account_type_2011_2_immo,f, -lu_2011_account_2021,2021,Frais de prospection,lu_2011_account_202,other,account_type_2011_2_immo,f, -lu_2011_account_2022,2022,Frais de publicité,lu_2011_account_202,other,account_type_2011_2_immo,f, -lu_2011_account_203,203,"Frais d'augmentation de capital et d'opérations diverses (fusions, scissions, transformations)",lu_2011_account_20,other,account_type_2011_2_immo,f, -lu_2011_account_204,204,Frais d'émission d'emprunts,lu_2011_account_20,other,account_type_2011_2_immo,f, -lu_2011_account_208,208,Autres frais assimilés,lu_2011_account_20,other,account_type_2011_2_immo,f, -lu_2011_account_21,21,Immobilisations incorporelles,lu_2011_account_2,view,account_type_2011_2_immo,f, -lu_2011_account_211,211,Frais de recherche et de développement,lu_2011_account_21,other,account_type_2011_2_immo,f,"account_financial_report_22,account_financial_report_abr_21" -lu_2011_account_212,212,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_21,view,account_type_2011_2_immo,f, -lu_2011_account_2121,2121,Acquis à titre onéreux (Actifs incorporels non produits),lu_2011_account_212,view,account_type_2011_2_immo,f,"account_financial_report_24,account_financial_report_abr_21" -lu_2011_account_21211,21211,Concessions,lu_2011_account_2121,other,account_type_2011_2_immo,f, -lu_2011_account_21212,21212,Brevets,lu_2011_account_2121,other,account_type_2011_2_immo,f, -lu_2011_account_21213,21213,Licences informatiques (logiciels et progiciels informatiques),lu_2011_account_2121,other,account_type_2011_2_immo,f, -lu_2011_account_21214,21214,Marques et franchises,lu_2011_account_2121,other,account_type_2011_2_immo,f, -lu_2011_account_21215,21215,Droits et valeurs similaires acquis à titre onéreux,lu_2011_account_2121,view,account_type_2011_2_immo,f, -lu_2011_account_212151,212151,Droits d'auteur et de reproduction,lu_2011_account_21215,other,account_type_2011_2_immo,f, -lu_2011_account_212152,212152,Droits d'émission,lu_2011_account_21215,other,account_type_2011_2_immo,f, -lu_2011_account_212158,212158,Autres droits et valeurs similaires acquis à titre onéreux,lu_2011_account_21215,other,account_type_2011_2_immo,f, -lu_2011_account_2122,2122,Créés par l'entreprise elle-même (Actifs incorporels produits),lu_2011_account_212,view,account_type_2011_2_immo,f,"account_financial_report_25,account_financial_report_abr_21" -lu_2011_account_21221,21221,Concessions,lu_2011_account_2122,other,account_type_2011_2_immo,f, -lu_2011_account_21222,21222,Brevets,lu_2011_account_2122,other,account_type_2011_2_immo,f, -lu_2011_account_21223,21223,Licences informatiques (logiciels et progiciels informatiques),lu_2011_account_2122,other,account_type_2011_2_immo,f, -lu_2011_account_21224,21224,Marques et franchises,lu_2011_account_2122,other,account_type_2011_2_immo,f, -lu_2011_account_21225,21225,Droits et valeurs similaires créés par l'entreprise elle-même,lu_2011_account_2122,view,account_type_2011_2_immo,f, -lu_2011_account_212251,212251,Droits d'auteur et de reproduction,lu_2011_account_21225,other,account_type_2011_2_immo,f, -lu_2011_account_212252,212252,Droits d'émission,lu_2011_account_21225,other,account_type_2011_2_immo,f, -lu_2011_account_212258,212258,Autres droits et valeurs similaires créés par l'entreprise elle-même,lu_2011_account_21225,other,account_type_2011_2_immo,f, -lu_2011_account_213,213,"Fonds de commerce, dans la mesure où il a été acquis à titre onéreux",lu_2011_account_21,other,account_type_2011_2_immo,f,"account_financial_report_26,account_financial_report_abr_21" -lu_2011_account_214,214,Acomptes versés et immobilisations incorporelles en cours,lu_2011_account_21,view,account_type_2011_2_immo,f,"account_financial_report_27,account_financial_report_abr_21" -lu_2011_account_2141,2141,Frais de recherche et de développement,lu_2011_account_214,other,account_type_2011_2_immo,f, -lu_2011_account_2142,2142,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_214,other,account_type_2011_2_immo,f, -lu_2011_account_2143,2143,Fonds de commerce,lu_2011_account_214,other,account_type_2011_2_immo,f, -lu_2011_account_22,22,Immobilisations corporelles,lu_2011_account_2,view,account_type_2011_2_immo,f, -lu_2011_account_221,221,Terrains et constructions,lu_2011_account_22,view,account_type_2011_2_immo,f,"account_financial_report_29,account_financial_report_abr_28" -lu_2011_account_2211,2211,Terrains,lu_2011_account_221,view,account_type_2011_2_immo,f, -lu_2011_account_22111,22111,Terrains nus,lu_2011_account_2211,other,account_type_2011_2_immo,f, -lu_2011_account_22112,22112,Terrains aménagés,lu_2011_account_2211,other,account_type_2011_2_immo,f, -lu_2011_account_22113,22113,Sous-sols et sursols,lu_2011_account_2211,other,account_type_2011_2_immo,f, -lu_2011_account_22114,22114,Terrains de gisement,lu_2011_account_2211,other,account_type_2011_2_immo,f, -lu_2011_account_22115,22115,Terrains bâtis,lu_2011_account_2211,other,account_type_2011_2_immo,f, -lu_2011_account_22118,22118,Autres terrains,lu_2011_account_2211,other,account_type_2011_2_immo,f, -lu_2011_account_2212,2212,Agencements et aménagements de terrains,lu_2011_account_221,view,account_type_2011_2_immo,f, -lu_2011_account_22121,22121,Agencements et aménagements de terrains nus,lu_2011_account_2212,other,account_type_2011_2_immo,f, -lu_2011_account_22122,22122,Agencements et aménagements de terrains aménagés,lu_2011_account_2212,other,account_type_2011_2_immo,f, -lu_2011_account_22123,22123,Agencements et aménagements de sous-sols et sursols,lu_2011_account_2212,other,account_type_2011_2_immo,f, -lu_2011_account_22124,22124,Agencements et aménagements de terrains de gisement,lu_2011_account_2212,other,account_type_2011_2_immo,f, -lu_2011_account_22125,22125,Agencements et aménagements de terrains bâtis,lu_2011_account_2212,other,account_type_2011_2_immo,f, -lu_2011_account_22128,22128,Agencements et aménagements d'autres terrains,lu_2011_account_2212,other,account_type_2011_2_immo,f, -lu_2011_account_2213,2213,Constructions,lu_2011_account_221,view,account_type_2011_2_immo,f, -lu_2011_account_22131,22131,Constructions sur sol propre,lu_2011_account_2213,other,account_type_2011_2_immo,f, -lu_2011_account_22132,22132,Constructions sur sol d'autrui,lu_2011_account_2213,other,account_type_2011_2_immo,f, -lu_2011_account_222,222,Installations techniques et machines,lu_2011_account_22,view,account_type_2011_2_immo,f,"account_financial_report_30,account_financial_report_abr_28" -lu_2011_account_2221,2221,Installations techniques,lu_2011_account_222,other,account_type_2011_2_immo,f, -lu_2011_account_2222,2222,Machines,lu_2011_account_222,other,account_type_2011_2_immo,f, -lu_2011_account_223,223,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_22,view,account_type_2011_2_immo,f,"account_financial_report_31,account_financial_report_abr_28" -lu_2011_account_2231,2231,Equipement de transport et de manutention,lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_2232,2232,Véhicules de transport,lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_2233,2233,Outillage,lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_2234,2234,Mobilier,lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_2235,2235,Matériel informatique (hardware),lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_2236,2236,Cheptel,lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_2237,2237,Emballages récupérables,lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_2238,2238,Autres installations,lu_2011_account_223,other,account_type_2011_2_immo,f, -lu_2011_account_224,224,Acomptes versés et immobilisations corporelles en cours,lu_2011_account_22,view,account_type_2011_2_immo,f,"account_financial_report_32,account_financial_report_abr_28" -lu_2011_account_2241,2241,Terrains et constructions,lu_2011_account_224,view,account_type_2011_2_immo,f, -lu_2011_account_22411,22411,Terrains,lu_2011_account_2241,other,account_type_2011_2_immo,f, -lu_2011_account_22412,22412,Agencements et aménagements de terrains,lu_2011_account_2241,other,account_type_2011_2_immo,f, -lu_2011_account_22413,22413,Constructions,lu_2011_account_2241,other,account_type_2011_2_immo,f, -lu_2011_account_2242,2242,Installations techniques et machines,lu_2011_account_224,other,account_type_2011_2_immo,f, -lu_2011_account_2243,2243,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_224,other,account_type_2011_2_immo,f, -lu_2011_account_23,23,Immobilisations financières,lu_2011_account_2,view,account_type_2011_2_immo,f, -lu_2011_account_231,231,Parts dans des entreprises liées,lu_2011_account_23,other,account_type_2011_2_immo,f,"account_financial_report_34,account_financial_report_abr_33" -lu_2011_account_232,232,Créances sur des entreprises liées,lu_2011_account_23,other,account_type_2011_2_immo,f,"account_financial_report_35,account_financial_report_abr_33" -lu_2011_account_233,233,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_23,other,account_type_2011_2_immo,f,"account_financial_report_36,account_financial_report_abr_33" -lu_2011_account_234,234,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_23,other,account_type_2011_2_immo,f,"account_financial_report_37,account_financial_report_abr_33" -lu_2011_account_235,235,Titres ayant le caractère d'immobilisations,lu_2011_account_23,view,account_type_2011_2_immo,f,"account_financial_report_38,account_financial_report_abr_33" -lu_2011_account_2351,2351,Titres immobilisés (droit de propriété),lu_2011_account_235,view,account_type_2011_2_immo,f, -lu_2011_account_23511,23511,Actions,lu_2011_account_2351,other,account_type_2011_2_immo,f, -lu_2011_account_23518,23518,Autres titres immobilisés (droit de propriété),lu_2011_account_2351,other,account_type_2011_2_immo,f, -lu_2011_account_2352,2352,Titres immobilisés (droit de créance),lu_2011_account_235,view,account_type_2011_2_immo,f, -lu_2011_account_23521,23521,Obligations,lu_2011_account_2352,other,account_type_2011_2_immo,f, -lu_2011_account_23528,23528,Autres titres immobilisés (droit de créance),lu_2011_account_2352,other,account_type_2011_2_immo,f, -lu_2011_account_2358,2358,Autres titres ayant le caractère d'immobilisations,lu_2011_account_235,other,account_type_2011_2_immo,f, -lu_2011_account_236,236,Prêts et créances immobilisées,lu_2011_account_23,view,account_type_2011_2_immo,f,"account_financial_report_39,account_financial_report_abr_33" -lu_2011_account_2361,2361,Prêts,lu_2011_account_236,view,account_type_2011_2_immo,f, -lu_2011_account_23611,23611,Prêts participatifs,lu_2011_account_2361,other,account_type_2011_2_immo,f, -lu_2011_account_23612,23612,Prêts aux associés,lu_2011_account_2361,other,account_type_2011_2_immo,f, -lu_2011_account_23613,23613,Prêts au personnel,lu_2011_account_2361,other,account_type_2011_2_immo,f, -lu_2011_account_23618,23618,Autres prêts,lu_2011_account_2361,other,account_type_2011_2_immo,f, -lu_2011_account_2362,2362,Dépôts et cautionnements versés,lu_2011_account_236,view,account_type_2011_2_immo,f, -lu_2011_account_23621,23621,Dépôts,lu_2011_account_2362,other,account_type_2011_2_immo,f, -lu_2011_account_23622,23622,Cautionnements,lu_2011_account_2362,other,account_type_2011_2_immo,f, -lu_2011_account_2363,2363,Créances immobilisées,lu_2011_account_236,other,account_type_2011_2_immo,f, -lu_2011_account_237,237,Actions propres ou parts propres,lu_2011_account_23,other,account_type_2011_2_immo,f,"account_financial_report_40,account_financial_report_abr_33" -lu_2011_account_3,3,CLASSE 3 - COMPTES DE STOCKS,lu_2011_account_bilan,view,account.data_account_type_view,f, -lu_2011_account_30,30,Matières premières et consommables,lu_2011_account_3,view,account_type_2011_3_stock,f,"account_financial_report_43,account_financial_report_abr_42" -lu_2011_account_301,301,Matières premières,lu_2011_account_30,other,account_type_2011_3_stock,f, -lu_2011_account_302,302,Matières consommables,lu_2011_account_30,other,account_type_2011_3_stock,f, -lu_2011_account_303,303,Fournitures consommables,lu_2011_account_30,view,account_type_2011_3_stock,f, -lu_2011_account_3031,3031,Combustibles,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_3032,3032,Produits d'entretien,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_3033,3033,Fournitures d'atelier et d'usine,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_3034,3034,Fournitures de magasin,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_3035,3035,Fournitures de bureau,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_3036,3036,Carburants,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_3037,3037,Lubrifiants,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_3038,3038,Autres fournitures consommables,lu_2011_account_303,other,account_type_2011_3_stock,f, -lu_2011_account_304,304,Emballages,lu_2011_account_30,view,account_type_2011_3_stock,f, -lu_2011_account_3041,3041,Emballages non-récupérables,lu_2011_account_304,other,account_type_2011_3_stock,f, -lu_2011_account_3042,3042,Emballages récupérables,lu_2011_account_304,other,account_type_2011_3_stock,f, -lu_2011_account_3043,3043,Emballages à usage mixte,lu_2011_account_304,other,account_type_2011_3_stock,f, -lu_2011_account_305,305,Approvisionnements,lu_2011_account_30,other,account_type_2011_3_stock,f, -lu_2011_account_31,31,Produits en cours de fabrication et commandes en cours,lu_2011_account_3,view,account_type_2011_3_stock,f,"account_financial_report_44,account_financial_report_abr_42" -lu_2011_account_311,311,Produits en cours de fabrication,lu_2011_account_31,other,account_type_2011_3_stock,f, -lu_2011_account_312,312,Commandes en cours – Produits,lu_2011_account_31,other,account_type_2011_3_stock,f, -lu_2011_account_313,313,Commandes en cours – Prestations de services,lu_2011_account_31,other,account_type_2011_3_stock,f, -lu_2011_account_314,314,Immeubles en construction,lu_2011_account_31,other,account_type_2011_3_stock,f, -lu_2011_account_32,32,Produits finis et marchandises,lu_2011_account_3,view,account_type_2011_3_stock,f,"account_financial_report_45,account_financial_report_abr_42" -lu_2011_account_321,321,Produits finis,lu_2011_account_32,other,account_type_2011_3_stock,f, -lu_2011_account_322,322,Produits intermédiaires,lu_2011_account_32,other,account_type_2011_3_stock,f, -lu_2011_account_323,323,Produits résiduels,lu_2011_account_32,view,account_type_2011_3_stock,f, -lu_2011_account_3231,3231,Déchets,lu_2011_account_323,other,account_type_2011_3_stock,f, -lu_2011_account_3232,3232,Rebuts,lu_2011_account_323,other,account_type_2011_3_stock,f, -lu_2011_account_3233,3233,Matières de récupération,lu_2011_account_323,other,account_type_2011_3_stock,f, -lu_2011_account_326,326,Marchandises,lu_2011_account_32,other,account_type_2011_3_stock,f, -lu_2011_account_327,327,"Marchandises en voie d'acheminement, mises en dépôt ou données en consignation",lu_2011_account_32,other,account_type_2011_3_stock,f, -lu_2011_account_33,33,Terrains et immeubles destinés à la revente,lu_2011_account_3,view,account_type_2011_3_stock,f,"account_financial_report_45,account_financial_report_abr_42" -lu_2011_account_331,331,Terrains,lu_2011_account_33,other,account_type_2011_3_stock,f, -lu_2011_account_332,332,Immeubles,lu_2011_account_33,view,account_type_2011_3_stock,f, -lu_2011_account_3321,3321,Immeubles acquis,lu_2011_account_332,other,account_type_2011_3_stock,f, -lu_2011_account_3322,3322,Immeubles construits,lu_2011_account_332,other,account_type_2011_3_stock,f, -lu_2011_account_34,34,Acomptes versés,lu_2011_account_3,view,account_type_2011_3_stock,f,"account_financial_report_46,account_financial_report_abr_42" -lu_2011_account_341,341,Acomptes versés sur matières premières et consommables,lu_2011_account_34,other,account_type_2011_3_stock,f, -lu_2011_account_342,342,Acomptes versés sur produits en cours de fabrication et commandes en cours,lu_2011_account_34,other,account_type_2011_3_stock,f, -lu_2011_account_343,343,Acomptes versés sur produits finis et marchandises,lu_2011_account_34,other,account_type_2011_3_stock,f, -lu_2011_account_344,344,Acomptes versés sur terrains et immeubles destinés à la revente,lu_2011_account_34,other,account_type_2011_3_stock,f, -lu_2011_account_4,4,CLASSE 4 - COMPTES DE TIERS,lu_2011_account_bilan,view,account.data_account_type_view,f, -lu_2011_account_40,40,Créances résultant de ventes et prestations de services,lu_2011_account_4,view,account_type_2011_4_creance,f, -lu_2011_account_401,401,Créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_40,view,account_type_2011_4_creance,f,"account_financial_report_49,account_financial_report_abr_49" -lu_2011_account_4011,4011,Clients,lu_2011_account_401,receivable,account_type_2011_4_creance,t, -lu_2011_account_4012,4012,Clients – Effets à recevoir,lu_2011_account_401,receivable,account_type_2011_4_creance,t, -lu_2011_account_4013,4013,Clients douteux ou litigieux,lu_2011_account_401,receivable,account_type_2011_4_creance,t, -lu_2011_account_4014,4014,Clients – Factures à établir,lu_2011_account_401,receivable,account_type_2011_4_creance,t, -lu_2011_account_4015,4015,Clients créditeurs,lu_2011_account_401,receivable,account_type_2011_4_creance,t, -lu_2011_account_4019,4019,Corrections de valeur,lu_2011_account_401,other,account_type_2011_4_creance,f, -lu_2011_account_402,402,Créances dont la durée résiduelle est supérieure à un an,lu_2011_account_40,view,account_type_2011_4_creance,f,"account_financial_report_50,account_financial_report_abr_50" -lu_2011_account_4021,4021,Clients,lu_2011_account_402,receivable,account_type_2011_4_creance,t, -lu_2011_account_4022,4022,Clients – Effets à recevoir,lu_2011_account_402,receivable,account_type_2011_4_creance,t, -lu_2011_account_4023,4023,Clients douteux ou litigieux,lu_2011_account_402,receivable,account_type_2011_4_creance,t, -lu_2011_account_4024,4024,Clients – Factures à établir,lu_2011_account_402,receivable,account_type_2011_4_creance,t, -lu_2011_account_4025,4025,Clients créditeurs,lu_2011_account_402,receivable,account_type_2011_4_creance,t, -lu_2011_account_4029,4029,Corrections de valeur,lu_2011_account_402,other,account_type_2011_4_creance,f, -lu_2011_account_41,41,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_4,view,account_type_2011_4_creance,f, -lu_2011_account_411,411,Créances sur des entreprises liées,lu_2011_account_41,view,account_type_2011_4_creance,f, -lu_2011_account_4111,4111,Créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_411,view,account_type_2011_4_creance,f,"account_financial_report_52,account_financial_report_abr_49" -lu_2011_account_41111,41111,Ventes de marchandises et de prestations de services,lu_2011_account_4111,other,account_type_2011_4_creance,f, -lu_2011_account_41112,41112,Prêts et avances,lu_2011_account_4111,other,account_type_2011_4_creance,f, -lu_2011_account_41113,41113,Intérêts courus,lu_2011_account_4111,other,account_type_2011_4_creance,f, -lu_2011_account_41114,41114,Dividendes à recevoir,lu_2011_account_4111,other,account_type_2011_4_creance,f, -lu_2011_account_41118,41118,Autres créances,lu_2011_account_4111,other,account_type_2011_4_creance,f, -lu_2011_account_41119,41119,Corrections de valeur,lu_2011_account_4111,other,account_type_2011_4_creance,f, -lu_2011_account_4112,4112,Créances dont la durée résiduelle est supérieure à un an,lu_2011_account_411,view,account_type_2011_4_creance,f,"account_financial_report_53,account_financial_report_abr_50" -lu_2011_account_41121,41121,Ventes de marchandises et de prestations de services,lu_2011_account_4112,other,account_type_2011_4_creance,f, -lu_2011_account_41122,41122,Prêts et avances,lu_2011_account_4112,other,account_type_2011_4_creance,f, -lu_2011_account_41123,41123,Intérêts courus,lu_2011_account_4112,other,account_type_2011_4_creance,f, -lu_2011_account_41124,41124,Dividendes à recevoir,lu_2011_account_4112,other,account_type_2011_4_creance,f, -lu_2011_account_41128,41128,Autres créances,lu_2011_account_4112,other,account_type_2011_4_creance,f, -lu_2011_account_41129,41129,Corrections de valeur,lu_2011_account_4112,other,account_type_2011_4_creance,f, -lu_2011_account_412,412,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_41,view,account_type_2011_4_creance,f, -lu_2011_account_4121,4121,Créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_412,view,account_type_2011_4_creance,f,"account_financial_report_55,account_financial_report_abr_49" -lu_2011_account_41211,41211,Ventes de marchandises et de prestations de service,lu_2011_account_4121,other,account_type_2011_4_creance,f, -lu_2011_account_41212,41212,Prêts et avances,lu_2011_account_4121,other,account_type_2011_4_creance,f, -lu_2011_account_41213,41213,Intérêts courus,lu_2011_account_4121,other,account_type_2011_4_creance,f, -lu_2011_account_41214,41214,Dividendes à recevoir,lu_2011_account_4121,other,account_type_2011_4_creance,f, -lu_2011_account_41218,41218,Autres créances,lu_2011_account_4121,other,account_type_2011_4_creance,f, -lu_2011_account_41219,41219,Corrections de valeur,lu_2011_account_4121,other,account_type_2011_4_creance,f, -lu_2011_account_4122,4122,Créances dont la durée résiduelle est supérieure à un an,lu_2011_account_412,view,account_type_2011_4_creance,f,"account_financial_report_56,account_financial_report_abr_50" -lu_2011_account_41221,41221,Ventes de marchandises et de prestations de service,lu_2011_account_4122,other,account_type_2011_4_creance,f, -lu_2011_account_41222,41222,Prêts et avances,lu_2011_account_4122,other,account_type_2011_4_creance,f, -lu_2011_account_41223,41223,Intérêts courus,lu_2011_account_4122,other,account_type_2011_4_creance,f, -lu_2011_account_41224,41224,Dividendes à recevoir,lu_2011_account_4122,other,account_type_2011_4_creance,f, -lu_2011_account_41228,41228,Autres créances,lu_2011_account_4122,other,account_type_2011_4_creance,f, -lu_2011_account_41229,41229,Corrections de valeur,lu_2011_account_4122,other,account_type_2011_4_creance,f, -lu_2011_account_42,42,Autres créances,lu_2011_account_4,view,account_type_2011_4_creance,f, -lu_2011_account_421,421,Autres créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_42,view,account_type_2011_4_creance,f,"account_financial_report_58,account_financial_report_abr_49" -lu_2011_account_4211,4211,Personnel – Avances et acomptes,lu_2011_account_421,view,account_type_2011_4_creance,f, -lu_2011_account_42111,42111,Avances et acomptes,lu_2011_account_4211,other,account_type_2011_4_creance,f, -lu_2011_account_42119,42119,Corrections de valeur,lu_2011_account_4211,other,account_type_2011_4_creance,f, -lu_2011_account_4212,4212,Créances sur associés ou actionnaires,lu_2011_account_421,view,account_type_2011_4_creance,f, -lu_2011_account_42121,42121,Montant principal,lu_2011_account_4212,other,account_type_2011_4_creance,f, -lu_2011_account_42122,42122,Intérêts courus,lu_2011_account_4212,other,account_type_2011_4_creance,f, -lu_2011_account_42129,42129,Corrections de valeur sur créances,lu_2011_account_4212,other,account_type_2011_4_creance,f, -lu_2011_account_4213,4213,Etat – Subventions à recevoir,lu_2011_account_421,view,account_type_2011_4_creance,f, -lu_2011_account_42131,42131,Subventions d'investissement,lu_2011_account_4213,other,account_type_2011_4_creance,f, -lu_2011_account_42132,42132,Subventions d'exploitation,lu_2011_account_4213,other,account_type_2011_4_creance,f, -lu_2011_account_42138,42138,Autres subventions,lu_2011_account_4213,other,account_type_2011_4_creance,f, -lu_2011_account_4214,4214,Administration des Contributions Directes (ACD),lu_2011_account_421,other,account_type_2011_4_creance,f, -lu_2011_account_4215,4215,Administration des Douanes et Accises (ADA),lu_2011_account_421,other,account_type_2011_4_creance,f, -lu_2011_account_4216,4216,Administration de l'Enregistrement et des Domaines (AED),lu_2011_account_421,view,account_type_2011_4_creance,f, -lu_2011_account_42161,42161,Taxe sur la valeur ajoutée – TVA,lu_2011_account_4216,view,account_type_2011_4_creance,f, -lu_2011_account_421611,421611,TVA en amont,lu_2011_account_42161,other,account_type_2011_4_creance,f, -lu_2011_account_421612,421612,TVA à recevoir,lu_2011_account_42161,other,account_type_2011_4_creance,f, -lu_2011_account_421613,421613,TVA acomptes versés,lu_2011_account_42161,other,account_type_2011_4_creance,f, -lu_2011_account_421618,421618,TVA – Autres créances,lu_2011_account_42161,other,account_type_2011_4_creance,f, -lu_2011_account_42162,42162,Impôts indirects,lu_2011_account_4216,view,account_type_2011_4_creance,f, -lu_2011_account_421621,421621,Droits d'enregistrement,lu_2011_account_42162,other,account_type_2011_4_creance,f, -lu_2011_account_421622,421622,Taxe d'abonnement,lu_2011_account_42162,other,account_type_2011_4_creance,f, -lu_2011_account_421623,421623,Droits d'hypothèques,lu_2011_account_42162,other,account_type_2011_4_creance,f, -lu_2011_account_421624,421624,Droits de timbre,lu_2011_account_42162,other,account_type_2011_4_creance,f, -lu_2011_account_421628,421628,Autres impôts indirects,lu_2011_account_42162,other,account_type_2011_4_creance,f, -lu_2011_account_42168,42168,AED – Autres créances,lu_2011_account_4216,other,account_type_2011_4_creance,f, -lu_2011_account_4217,4217,Créances sur la sécurité sociale et autres organismes sociaux,lu_2011_account_421,view,account_type_2011_4_creance,f, -lu_2011_account_42171,42171,Centre Commun de Sécurité Sociale (CCSS),lu_2011_account_4217,other,account_type_2011_4_creance,f, -lu_2011_account_42172,42172,Mutualité des employeurs,lu_2011_account_4217,other,account_type_2011_4_creance,f, -lu_2011_account_42178,42178,Autres organismes sociaux,lu_2011_account_4217,other,account_type_2011_4_creance,f, -lu_2011_account_4218,4218,Créances diverses,lu_2011_account_421,view,account_type_2011_4_creance,f, -lu_2011_account_42181,42181,Impôts étrangers,lu_2011_account_4218,view,account_type_2011_4_creance,f, -lu_2011_account_421811,421811,TVA étrangères,lu_2011_account_42181,other,account_type_2011_4_creance,f, -lu_2011_account_421818,421818,Autres impôts étrangers,lu_2011_account_42181,other,account_type_2011_4_creance,f, -lu_2011_account_42188,42188,Autres créances diverses,lu_2011_account_4218,other,account_type_2011_4_creance,f, -lu_2011_account_42189,42189,Corrections de valeur,lu_2011_account_4218,other,account_type_2011_4_creance,f, -lu_2011_account_422,422,Autres créances dont la durée résiduelle est supérieure à un an,lu_2011_account_42,view,account_type_2011_4_creance,f,"account_financial_report_59,account_financial_report_abr_50" -lu_2011_account_4221,4221,Personnel – Avances et acomptes,lu_2011_account_422,view,account_type_2011_4_creance,f, -lu_2011_account_42211,42211,Avances et acomptes,lu_2011_account_4221,other,account_type_2011_4_creance,f, -lu_2011_account_42219,42219,Corrections de valeur,lu_2011_account_4221,other,account_type_2011_4_creance,f, -lu_2011_account_4222,4222,Associés ou actionnaires,lu_2011_account_422,view,account_type_2011_4_creance,f, -lu_2011_account_42221,42221,Montant principal,lu_2011_account_4222,other,account_type_2011_4_creance,f, -lu_2011_account_42222,42222,Intérêts courus,lu_2011_account_4222,other,account_type_2011_4_creance,f, -lu_2011_account_42229,42229,Corrections de valeur sur créances,lu_2011_account_4222,other,account_type_2011_4_creance,f, -lu_2011_account_4223,4223,Etat – Subventions à recevoir,lu_2011_account_422,view,account_type_2011_4_creance,f, -lu_2011_account_42231,42231,Subventions d'investissement,lu_2011_account_4223,other,account_type_2011_4_creance,f, -lu_2011_account_42232,42232,Subventions d'exploitation,lu_2011_account_4223,other,account_type_2011_4_creance,f, -lu_2011_account_42238,42238,Autres subventions,lu_2011_account_4223,other,account_type_2011_4_creance,f, -lu_2011_account_4224,4224,Administration des Contributions Directes (ACD),lu_2011_account_422,other,account_type_2011_4_creance,f, -lu_2011_account_4225,4225,Administration des Douanes et Accises (ADA),lu_2011_account_422,other,account_type_2011_4_creance,f, -lu_2011_account_4226,4226,Administration de l'Enregistrement et des Domaines (AED),lu_2011_account_422,view,account_type_2011_4_creance,f, -lu_2011_account_42261,42261,Taxe sur la valeur ajoutée – TVA,lu_2011_account_4226,view,account_type_2011_4_creance,f, -lu_2011_account_422611,422611,TVA en amont,lu_2011_account_42261,view,account_type_2011_4_creance,f, -lu_2011_account_4226111,4226111,TVA en amont – Pays,lu_2011_account_422611,other,account_type_2011_4_creance,f, -lu_2011_account_4226112,4226112,TVA en amont – Intracommunautaire,lu_2011_account_422611,other,account_type_2011_4_creance,f, -lu_2011_account_4226113,4226113,TVA en amont – Triangulaire,lu_2011_account_422611,other,account_type_2011_4_creance,f, -lu_2011_account_4226114,4226114,TVA en amont – Exonérations spéciales,lu_2011_account_422611,other,account_type_2011_4_creance,f, -lu_2011_account_422612,422612,TVA à recevoir,lu_2011_account_42261,other,account_type_2011_4_creance,f, -lu_2011_account_422613,422613,TVA acomptes versés,lu_2011_account_42261,other,account_type_2011_4_creance,f, -lu_2011_account_422618,422618,TVA – Autres créances,lu_2011_account_42261,other,account_type_2011_4_creance,f, -lu_2011_account_42262,42262,Impôts indirects,lu_2011_account_4226,view,account_type_2011_4_creance,f, -lu_2011_account_422621,422621,Droits d'enregistrement,lu_2011_account_42262,other,account_type_2011_4_creance,f, -lu_2011_account_422622,422622,Taxe d'abonnement,lu_2011_account_42262,other,account_type_2011_4_creance,f, -lu_2011_account_422623,422623,Droits d'hypothèques,lu_2011_account_42262,other,account_type_2011_4_creance,f, -lu_2011_account_422624,422624,Droits de timbre,lu_2011_account_42262,other,account_type_2011_4_creance,f, -lu_2011_account_422628,422628,Autres impôts indirects,lu_2011_account_42262,other,account_type_2011_4_creance,f, -lu_2011_account_4227,4227,Créances sur la sécurité sociale et autres organismes sociaux,lu_2011_account_422,view,account_type_2011_4_creance,f, -lu_2011_account_42271,42271,Centre Commun de Sécurité Sociale (CCSS),lu_2011_account_4227,other,account_type_2011_4_creance,f, -lu_2011_account_42272,42272,Mutualité des employeurs,lu_2011_account_4227,other,account_type_2011_4_creance,f, -lu_2011_account_42278,42278,Autres organismes sociaux,lu_2011_account_4227,other,account_type_2011_4_creance,f, -lu_2011_account_4228,4228,Créances diverses,lu_2011_account_422,view,account_type_2011_4_creance,f, -lu_2011_account_42281,42281,Impôts étrangers,lu_2011_account_4228,view,account_type_2011_4_creance,f, -lu_2011_account_422811,422811,TVA étrangères,lu_2011_account_42281,other,account_type_2011_4_creance,f, -lu_2011_account_422818,422818,Autres impôts étrangers,lu_2011_account_42281,other,account_type_2011_4_creance,f, -lu_2011_account_42288,42288,Autres créances diverses,lu_2011_account_4228,other,account_type_2011_4_creance,f, -lu_2011_account_42289,42289,Corrections de valeur sur autres créances diverses,lu_2011_account_4228,other,account_type_2011_4_creance,f, -lu_2011_account_43,43,Acomptes reçus sur commandes pour autant qu'ils ne sont pas déduits des stocks de façon distincte,lu_2011_account_4,view,account_type_2011_4_dette,f, -lu_2011_account_431,431,Acomptes reçus dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_43,other,account_type_2011_4_dette,f,"account_financial_report_98,account_financial_report_abr_89" -lu_2011_account_432,432,Acomptes reçus dont la durée résiduelle est supérieure à un an,lu_2011_account_43,other,account_type_2011_4_dette,f,"account_financial_report_99,account_financial_report_abr_90" -lu_2011_account_44,44,Dettes sur achats et prestations de services et dettes représentées par des effets de commerce,lu_2011_account_4,view,account_type_2011_4_dette,f, -lu_2011_account_441,441,Dettes sur achats et prestations de services,lu_2011_account_44,view,account_type_2011_4_dette,f, -lu_2011_account_4411,4411,Dettes sur achats et prestations de services dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_441,view,account_type_2011_4_dette,f,"account_financial_report_101,account_financial_report_abr_89" -lu_2011_account_44111,44111,Fournisseurs,lu_2011_account_4411,payable,account_type_2011_4_dette,t, -lu_2011_account_44112,44112,Fournisseurs – Factures non parvenues,lu_2011_account_4411,payable,account_type_2011_4_dette,t, -lu_2011_account_44113,44113,Fournisseurs débiteurs,lu_2011_account_4411,view,account_type_2011_4_dette,f, -lu_2011_account_441131,441131,Fournisseurs – Avances et acomptes versés sur commandes,lu_2011_account_44113,payable,account_type_2011_4_dette,t, -lu_2011_account_441132,441132,Fournisseurs – Créances pour emballages et matériel à rendre,lu_2011_account_44113,payable,account_type_2011_4_dette,t, -lu_2011_account_441133,441133,Fournisseurs – Autres avoirs,lu_2011_account_44113,payable,account_type_2011_4_dette,t, -lu_2011_account_441134,441134,"Rabais, remises, ristournes à obtenir et autres avoirs non encore reçus",lu_2011_account_44113,payable,account_type_2011_4_dette,t, -lu_2011_account_4412,4412,Dettes sur achats et prestations de services dont la durée résiduelle est supérieure à un an,lu_2011_account_441,view,account_type_2011_4_dette,f,"account_financial_report_102,account_financial_report_abr_90" -lu_2011_account_44121,44121,Fournisseurs,lu_2011_account_4412,payable,account_type_2011_4_dette,t, -lu_2011_account_44122,44122,Fournisseurs – Factures non parvenues,lu_2011_account_4412,payable,account_type_2011_4_dette,t, -lu_2011_account_44123,44123,Fournisseurs débiteurs,lu_2011_account_4412,view,account_type_2011_4_dette,f, -lu_2011_account_441231,441231,Fournisseurs – Avances et acomptes versés sur commandes,lu_2011_account_44123,payable,account_type_2011_4_dette,t, -lu_2011_account_441232,441232,Fournisseurs – Créances pour emballages et matériel à rendre,lu_2011_account_44123,payable,account_type_2011_4_dette,t, -lu_2011_account_441233,441233,Fournisseurs – Autres avoirs,lu_2011_account_44123,payable,account_type_2011_4_dette,t, -lu_2011_account_441234,441234,"Rabais, remises, ristournes à obtenir et autres avoirs non encore reçus",lu_2011_account_44123,payable,account_type_2011_4_dette,t, -lu_2011_account_442,442,Dettes représentées par des effets de commerce,lu_2011_account_44,view,account_type_2011_4_dette,f, -lu_2011_account_4421,4421,Dettes représentées par des effets de commerce dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_442,other,account_type_2011_4_dette,f,"account_financial_report_104,account_financial_report_abr_89" -lu_2011_account_4422,4422,Dettes représentées par des effets de commerce dont la durée résiduelle est supérieure à un an,lu_2011_account_442,other,account_type_2011_4_dette,f,"account_financial_report_105,account_financial_report_abr_90" -lu_2011_account_45,45,Dettes envers des entreprises liées et des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_4,view,account_type_2011_4_dette,f, -lu_2011_account_451,451,Dettes envers des entreprises liées,lu_2011_account_45,view,account_type_2011_4_dette,f, -lu_2011_account_4511,4511,Dettes envers des entreprises liées dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_451,view,account_type_2011_4_dette,f,"account_financial_report_107,account_financial_report_abr_89" -lu_2011_account_45111,45111,Ventes de marchandises et de prestations de services,lu_2011_account_4511,other,account_type_2011_4_dette,f, -lu_2011_account_45112,45112,Prêts et avances,lu_2011_account_4511,other,account_type_2011_4_dette,f, -lu_2011_account_45113,45113,Intérêts courus,lu_2011_account_4511,other,account_type_2011_4_dette,f, -lu_2011_account_45114,45114,Dividendes à payer,lu_2011_account_4511,other,account_type_2011_4_dette,f, -lu_2011_account_45118,45118,Autres dettes,lu_2011_account_4511,other,account_type_2011_4_dette,f, -lu_2011_account_4512,4512,Dettes envers des entreprises liées dont la durée résiduelle est supérieure à un an,lu_2011_account_451,view,account_type_2011_4_dette,f,"account_financial_report_108,account_financial_report_abr_90" -lu_2011_account_45121,45121,Ventes de marchandises et de prestations de services,lu_2011_account_4512,other,account_type_2011_4_dette,f, -lu_2011_account_45122,45122,Prêts et avances,lu_2011_account_4512,other,account_type_2011_4_dette,f, -lu_2011_account_45123,45123,Intérêts courus,lu_2011_account_4512,other,account_type_2011_4_dette,f, -lu_2011_account_45124,45124,Dividendes à payer,lu_2011_account_4512,other,account_type_2011_4_dette,f, -lu_2011_account_45128,45128,Autres dettes,lu_2011_account_4512,other,account_type_2011_4_dette,f, -lu_2011_account_452,452,Dettes envers des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_45,view,account_type_2011_4_dette,f, -lu_2011_account_4521,4521,Dettes envers des entreprises avec lesquelles la société a un lien de participation dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_452,view,account_type_2011_4_dette,f,"account_financial_report_110,account_financial_report_abr_89" -lu_2011_account_45211,45211,Ventes de marchandises et de prestations de services,lu_2011_account_4521,other,account_type_2011_4_dette,f, -lu_2011_account_45212,45212,Prêts et avances,lu_2011_account_4521,other,account_type_2011_4_dette,f, -lu_2011_account_45213,45213,Intérêts courus,lu_2011_account_4521,other,account_type_2011_4_dette,f, -lu_2011_account_45214,45214,Dividendes à payer,lu_2011_account_4521,other,account_type_2011_4_dette,f, -lu_2011_account_45218,45218,Autres dettes,lu_2011_account_4521,other,account_type_2011_4_dette,f, -lu_2011_account_4522,4522,Dettes envers des entreprises avec lesquelles la société a un lien de participation dont la durée résiduelle est supérieure à un an,lu_2011_account_452,view,account_type_2011_4_dette,f,"account_financial_report_111,account_financial_report_abr_90" -lu_2011_account_45221,45221,Ventes de marchandises et de prestations de services,lu_2011_account_4522,other,account_type_2011_4_dette,f, -lu_2011_account_45222,45222,Prêts et avances,lu_2011_account_4522,other,account_type_2011_4_dette,f, -lu_2011_account_45223,45223,Intérêts courus,lu_2011_account_4522,other,account_type_2011_4_dette,f, -lu_2011_account_45224,45224,Dividendes à payer,lu_2011_account_4522,other,account_type_2011_4_dette,f, -lu_2011_account_45228,45228,Autres dettes,lu_2011_account_4522,other,account_type_2011_4_dette,f, -lu_2011_account_46,46,Dettes fiscales et dettes envers la sécurité sociale,lu_2011_account_4,view,account_type_2011_4_dette,f, -lu_2011_account_461,461,Dettes fiscales,lu_2011_account_46,view,account_type_2011_4_dette,f,"account_financial_report_113,account_financial_report_abr_89" -lu_2011_account_4611,4611,Administrations communales,lu_2011_account_461,view,account_type_2011_4_dette,f, -lu_2011_account_46111,46111,Impôts communaux,lu_2011_account_4611,other,account_type_2011_4_dette,f, -lu_2011_account_46112,46112,Taxes communales,lu_2011_account_4611,other,account_type_2011_4_dette,f, -lu_2011_account_4612,4612,Administration des Contributions Directes (ACD),lu_2011_account_461,view,account_type_2011_4_dette,f, -lu_2011_account_46121,46121,Impôt sur le revenu des collectivités,lu_2011_account_4612,view,account_type_2011_4_dette,f, -lu_2011_account_461211,461211,Impôt sur le revenu des collectivités – charge fiscale estimée,lu_2011_account_46121,other,account_type_2011_4_dette,f, -lu_2011_account_461212,461212,Impôt sur le revenu des collectivités – dette fiscale à payer,lu_2011_account_46121,other,account_type_2011_4_dette,f, -lu_2011_account_46122,46122,Impôt commercial,lu_2011_account_4612,view,account_type_2011_4_dette,f, -lu_2011_account_461221,461221,Impôt commercial – charge fiscale estimée,lu_2011_account_46122,other,account_type_2011_4_dette,f, -lu_2011_account_461222,461222,Impôt commercial – dette fiscale à payer,lu_2011_account_46122,other,account_type_2011_4_dette,f, -lu_2011_account_46123,46123,Impôt sur la fortune,lu_2011_account_4612,view,account_type_2011_4_dette,f, -lu_2011_account_461231,461231,Impôt sur la fortune – charge fiscale estimée,lu_2011_account_46123,other,account_type_2011_4_dette,f, -lu_2011_account_461232,461232,Impôt sur la fortune – dette fiscale à payer,lu_2011_account_46123,other,account_type_2011_4_dette,f, -lu_2011_account_46124,46124,Retenue d'impôt sur traitements et salaires,lu_2011_account_4612,other,account_type_2011_4_dette,f, -lu_2011_account_46125,46125,Retenue d'impôt sur revenus de capitaux mobiliers,lu_2011_account_4612,other,account_type_2011_4_dette,f, -lu_2011_account_46126,46126,Retenue d'impôt sur les tantièmes,lu_2011_account_4612,other,account_type_2011_4_dette,f, -lu_2011_account_46128,46128,ACD – Autres dettes,lu_2011_account_4612,other,account_type_2011_4_dette,f, -lu_2011_account_4613,4613,Administration des Douanes et Accises (ADA),lu_2011_account_461,view,account_type_2011_4_dette,f, -lu_2011_account_46131,46131,Taxe sur les véhicules automoteurs,lu_2011_account_4613,other,account_type_2011_4_dette,f, -lu_2011_account_46132,46132,Droits d'accises et taxe de consommation,lu_2011_account_4613,other,account_type_2011_4_dette,f, -lu_2011_account_46138,46138,ADA – Autres dettes,lu_2011_account_4613,other,account_type_2011_4_dette,f, -lu_2011_account_4614,4614,Administration de l'Enregistrement et des Domaines (AED),lu_2011_account_461,view,account_type_2011_4_dette,f, -lu_2011_account_46141,46141,Taxe sur la valeur ajoutée – TVA,lu_2011_account_4614,view,account_type_2011_4_dette,f, -lu_2011_account_461411,461411,TVA en aval,lu_2011_account_46141,view,account_type_2011_4_dette,f, -lu_2011_account_4614111,4614111,TVA en aval – Pays,lu_2011_account_461411,other,account_type_2011_4_dette,f, -lu_2011_account_4614112,4614112,TVA en aval – Intracommunautaire,lu_2011_account_461411,other,account_type_2011_4_dette,f, -lu_2011_account_4614113,4614113,TVA en aval – Extracommunautaire,lu_2011_account_461411,other,account_type_2011_4_dette,f, -lu_2011_account_4614114,4614114,TVA en aval – Triangulaire,lu_2011_account_461411,other,account_type_2011_4_dette,f, -lu_2011_account_4614115,4614115,TVA en aval – Exonérations spéciales,lu_2011_account_461411,other,account_type_2011_4_dette,f, -lu_2011_account_461412,461412,TVA due,lu_2011_account_46141,other,account_type_2011_4_dette,f, -lu_2011_account_461413,461413,TVA acomptes reçus,lu_2011_account_46141,other,account_type_2011_4_dette,f, -lu_2011_account_461418,461418,TVA – Autres dettes,lu_2011_account_46141,other,account_type_2011_4_dette,f, -lu_2011_account_46142,46142,Impôts indirects,lu_2011_account_4614,view,account_type_2011_4_dette,f, -lu_2011_account_461421,461421,Droits d'enregistrement,lu_2011_account_46142,other,account_type_2011_4_dette,f, -lu_2011_account_461422,461422,Taxe d'abonnement,lu_2011_account_46142,other,account_type_2011_4_dette,f, -lu_2011_account_461423,461423,Droits d'hypothèques,lu_2011_account_46142,other,account_type_2011_4_dette,f, -lu_2011_account_461424,461424,Droits de timbre,lu_2011_account_46142,other,account_type_2011_4_dette,f, -lu_2011_account_461428,461428,Autres impôts indirects,lu_2011_account_46142,other,account_type_2011_4_dette,f, -lu_2011_account_4615,4615,Administrations fiscales étrangères,lu_2011_account_461,other,account_type_2011_4_dette,f, -lu_2011_account_462,462,Dettes au titre de la sécurité sociale,lu_2011_account_46,view,account_type_2011_4_dette,f,"account_financial_report_114,account_financial_report_abr_90" -lu_2011_account_4621,4621,Centre Commun de Sécurité Sociale (CCSS),lu_2011_account_462,other,account_type_2011_4_dette,f, -lu_2011_account_4622,4622,Organismes de sécurité sociale étrangers,lu_2011_account_462,other,account_type_2011_4_dette,f, -lu_2011_account_4628,4628,Autres organismes sociaux,lu_2011_account_462,other,account_type_2011_4_dette,f, -lu_2011_account_47,47,Autres dettes,lu_2011_account_4,view,account_type_2011_4_dette,f, -lu_2011_account_471,471,Autres dettes dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_47,view,account_type_2011_4_dette,f,"account_financial_report_116,account_financial_report_abr_89" -lu_2011_account_4711,4711,Dépôts et cautionnements reçus,lu_2011_account_471,view,account_type_2011_4_dette,f, -lu_2011_account_47111,47111,Dépôts,lu_2011_account_4711,other,account_type_2011_4_dette,f, -lu_2011_account_47112,47112,Cautionnements,lu_2011_account_4711,other,account_type_2011_4_dette,f, -lu_2011_account_47113,47113,Intérêts courus,lu_2011_account_4711,other,account_type_2011_4_dette,f, -lu_2011_account_4712,4712,Dettes envers associés et actionnaires,lu_2011_account_471,view,account_type_2011_4_dette,f, -lu_2011_account_47121,47121,Montant principal,lu_2011_account_4712,other,account_type_2011_4_dette,f, -lu_2011_account_47122,47122,Intérêts courus,lu_2011_account_4712,other,account_type_2011_4_dette,f, -lu_2011_account_4713,4713,"Dettes envers administrateurs, gérants et commissaires",lu_2011_account_471,other,account_type_2011_4_dette,f, -lu_2011_account_4714,4714,Dettes envers le personnel,lu_2011_account_471,view,account_type_2011_4_dette,f, -lu_2011_account_47141,47141,Personnel – Rémunérations dues,lu_2011_account_4714,other,account_type_2011_4_dette,f, -lu_2011_account_47142,47142,Personnel – Dépôts,lu_2011_account_4714,other,account_type_2011_4_dette,f, -lu_2011_account_47143,47143,"Personnel – Oppositions, saisies",lu_2011_account_4714,other,account_type_2011_4_dette,f, -lu_2011_account_47148,47148,Personnel – Autres,lu_2011_account_4714,other,account_type_2011_4_dette,f, -lu_2011_account_4715,4715,Etat – Droits d'émission à restituer,lu_2011_account_471,other,account_type_2011_4_dette,f, -lu_2011_account_4718,4718,Autres dettes diverses,lu_2011_account_471,other,account_type_2011_4_dette,f, -lu_2011_account_472,472,Autres dettes dont la durée résiduelle est supérieure à un an,lu_2011_account_47,view,account_type_2011_4_dette,f,"account_financial_report_117,account_financial_report_abr_90" -lu_2011_account_4721,4721,Dépôts et cautionnements reçus,lu_2011_account_472,view,account_type_2011_4_dette,f, -lu_2011_account_47211,47211,Dépôts,lu_2011_account_4721,other,account_type_2011_4_dette,f, -lu_2011_account_47212,47212,Cautionnements,lu_2011_account_4721,other,account_type_2011_4_dette,f, -lu_2011_account_47213,47213,Intérêts courus,lu_2011_account_4721,other,account_type_2011_4_dette,f, -lu_2011_account_4722,4722,Dettes envers associés et actionnaires,lu_2011_account_472,view,account_type_2011_4_dette,f, -lu_2011_account_47221,47221,Montant principal,lu_2011_account_4722,other,account_type_2011_4_dette,f, -lu_2011_account_47222,47222,Intérêts courus,lu_2011_account_4722,other,account_type_2011_4_dette,f, -lu_2011_account_4723,4723,"Dettes envers administrateurs, gérants et commissaires",lu_2011_account_472,other,account_type_2011_4_dette,f, -lu_2011_account_4724,4724,Dettes envers le personnel,lu_2011_account_472,view,account_type_2011_4_dette,f, -lu_2011_account_47241,47241,Personnel – Rémunérations dues,lu_2011_account_4724,other,account_type_2011_4_dette,f, -lu_2011_account_47242,47242,Personnel – Dépôts,lu_2011_account_4724,other,account_type_2011_4_dette,f, -lu_2011_account_47243,47243,"Personnel – Oppositions, saisies",lu_2011_account_4724,other,account_type_2011_4_dette,f, -lu_2011_account_47248,47248,Personnel – Autres,lu_2011_account_4724,other,account_type_2011_4_dette,f, -lu_2011_account_4726,4726,Etat – Droits d'émission à restituer,lu_2011_account_472,other,account_type_2011_4_dette,f, -lu_2011_account_4728,4728,Autres dettes diverses,lu_2011_account_472,other,account_type_2011_4_dette,f, -lu_2011_account_48,48,Comptes de régularisation,lu_2011_account_4,view,account.data_account_type_view,f, -lu_2011_account_481,481,Charges à reporter,lu_2011_account_48,other,account_type_2011_4_regul_actif,f,"account_financial_report_65,account_financial_report_abr_65" -lu_2011_account_482,482,Produits à reporter,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,"account_financial_report_118,account_financial_report_abr_118" -lu_2011_account_483,483,Etat - Droits d'émission alloués,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,"account_financial_report_118,account_financial_report_abr_118" -lu_2011_account_484,484,Comptes transitoires ou d'attente – Actif,lu_2011_account_48,other,account_type_2011_4_regul_actif,f,"account_financial_report_65,account_financial_report_abr_65" -lu_2011_account_485,485,Comptes transitoires ou d'attente – Passif,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,"account_financial_report_118,account_financial_report_abr_118" -lu_2011_account_486,486,Comptes de liaison – Actif,lu_2011_account_48,other,account_type_2011_4_regul_actif,f,"account_financial_report_65,account_financial_report_abr_65" -lu_2011_account_487,487,Comptes de liaison – Passif,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,"account_financial_report_118,account_financial_report_abr_118" -lu_2011_account_5,5,CLASSE 5 - COMPTES FINANCIERS,lu_2011_account_bilan,view,account.data_account_type_view,f, -lu_2011_account_50,50,Valeurs mobilières,lu_2011_account_5,view,account_type_2011_5_disponibilite,f, -lu_2011_account_501,501,Parts dans des entreprises liées,lu_2011_account_50,other,account_type_2011_5_disponibilite,f,"account_financial_report_61,account_financial_report_abr_60" -lu_2011_account_502,502,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_50,other,account_type_2011_5_disponibilite,f,"account_financial_report_61,account_financial_report_abr_60" -lu_2011_account_503,503,Actions propres ou parts propres,lu_2011_account_50,other,account_type_2011_5_disponibilite,f,"account_financial_report_62,account_financial_report_abr_60" -lu_2011_account_508,508,Autres valeurs mobilières,lu_2011_account_50,view,account_type_2011_5_disponibilite,f,"account_financial_report_63,account_financial_report_abr_60" -lu_2011_account_5081,5081,Actions – Titres cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, -lu_2011_account_5082,5082,Actions – Titres non cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, -lu_2011_account_5083,5083,Obligations et autres titres de créance émis par la société et rachetés par elle,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, -lu_2011_account_5084,5084,Obligations – Titres cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, -lu_2011_account_5085,5085,Obligations – Titres non cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, -lu_2011_account_5088,5088,Autres valeurs mobilières diverses,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, -lu_2011_account_51,51,"Avoirs en banques, avoirs en comptes de chèques postaux, chèques et encaisse",lu_2011_account_5,view,account_type_2011_5_disponibilite,f,"account_financial_report_64,account_financial_report_abr_64" -lu_2011_account_511,511,Chèques à encaisser,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, -lu_2011_account_512,512,Valeurs à l'encaissement,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, -lu_2011_account_513,513,Banques,lu_2011_account_51,view,account_type_2011_5_disponibilite,f, -lu_2011_account_5131,5131,Banques comptes courants,lu_2011_account_513,view,account_type_2011_5_disponibilite,f, -lu_2011_account_5132,5132,Banques comptes à terme,lu_2011_account_513,view,account_type_2011_5_disponibilite,f, -lu_2011_account_514,514,Compte chèque postal,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, -lu_2011_account_516,516,Caisse,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, -lu_2011_account_517,517,Virements internes,lu_2011_account_51,other,account_type_2011_5_disponibilite,t, -lu_2011_account_518,518,Autres avoirs,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, -lu_2011_account_resultat,resultat,TOTAL CLASSES 6 ET 7,lu_2011_account_0,view,account.data_account_type_view,,"account_financial_report_77,account_financial_report_161,account_financial_report_abr_77,account_financial_report_abr_161" -lu_2011_account_6,6,CLASSE 6 - COMPTES DE CHARGES,lu_2011_account_resultat,view,account.data_account_type_view,f, -lu_2011_account_60,60,Consommation de marchandises et de matières premières et consommables,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_120,account_financial_report_abr_120,account_financial_report_abr_141" -lu_2011_account_601,601,Matières premières,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_602,602,Matières consommables,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_603,603,Fournitures consommables,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6031,6031,Combustibles,lu_2011_account_603,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60311,60311,Solides,lu_2011_account_6031,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60312,60312,Liquides,lu_2011_account_6031,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60313,60313,Gaz comprimé,lu_2011_account_6031,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6032,6032,Produits d'entretien,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6033,6033,Fournitures d'atelier et d'usine,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6034,6034,Fournitures de magasin,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6035,6035,Fournitures de bureau,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6036,6036,Carburants,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6037,6037,Lubrifiants,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6038,6038,Autres fournitures consommables,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_604,604,Emballages,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6041,6041,Emballages non récupérables,lu_2011_account_604,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6042,6042,Emballages récupérables,lu_2011_account_604,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6043,6043,Emballages à usage mixte,lu_2011_account_604,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_605,605,Approvisionnements,lu_2011_account_60,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_606,606,Achats de biens destinés à la revente,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6061,6061,Terrains,lu_2011_account_606,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6062,6062,Immeubles,lu_2011_account_606,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6063,6063,Marchandises,lu_2011_account_606,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_607,607,Variation des stocks,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6071,6071,Variation des stocks de matières premières,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6072,6072,Variation des stocks de matières consommables,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6073,6073,Variation des stocks de fournitures consommables,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6074,6074,Variation des stocks d'emballages,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6075,6075,Variation des stocks d'approvisionnements,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6076,6076,Variation des stocks de biens destinés à la revente,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_608,608,Achats non stockés et achats incorporés aux ouvrages et produits,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6081,6081,Achats non stockés de matières et fournitures,lu_2011_account_608,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60811,60811,Fournitures non stockables,lu_2011_account_6081,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_608111,608111,Eau,lu_2011_account_60811,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_608112,608112,Electricité,lu_2011_account_60811,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_608113,608113,Gaz de canalisation,lu_2011_account_60811,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60812,60812,Fournitures d'entretien et de petit équipement,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60813,60813,Fournitures administratives,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60814,60814,Carburants,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60815,60815,Lubrifiants,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60816,60816,Vêtements professionnels,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60818,60818,Autres matières et fournitures non stockées,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6082,6082,Achats incorporés aux ouvrages et produits,lu_2011_account_608,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60821,60821,Achats d'études et prestations de service (incorporés aux ouvrages et produits),lu_2011_account_6082,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_608211,608211,Travail à façon,lu_2011_account_60821,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_608212,608212,Recherche et développement,lu_2011_account_60821,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_608213,608213,Frais d'architectes et d'ingénieurs,lu_2011_account_60821,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60822,60822,"Achats de matériel, équipements, pièces détachées et travaux (incorporés aux ouvrages et produits)",lu_2011_account_6082,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_60828,60828,Autres achats d'études et de prestations de service,lu_2011_account_6082,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_609,609,"Rabais, remises et ristournes obtenus",lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6091,6091,Matières premières,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6092,6092,Matières consommables,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6093,6093,Fournitures consommables,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6094,6094,Emballages,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6095,6095,Approvisionnements,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6096,6096,Achats de biens destinés à la revente,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6098,6098,Achats non stockés et achats incorporés aux ouvrages et produits,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6099,6099,"Rabais, remises et ristournes non affectés",lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61,61,Autres charges externes,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_121,account_financial_report_abr_120,account_financial_report_abr_141" -lu_2011_account_611,611,Loyers et charges locatives,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6111,6111,Locations immobilières,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61111,61111,Terrains,lu_2011_account_6111,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61112,61112,Bâtiments,lu_2011_account_6111,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6112,6112,Locations mobilières,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61121,61121,Installations techniques et machines,lu_2011_account_6112,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61122,61122,"Autres installations, outillages et machines",lu_2011_account_6112,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61123,61123,Matériel roulant,lu_2011_account_6112,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6113,6113,Charges locatives et de copropriété,lu_2011_account_611,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6114,6114,Leasing immobilier,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61141,61141,Terrains,lu_2011_account_6114,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61142,61142,Bâtiments,lu_2011_account_6114,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6115,6115,Leasing mobilier,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61151,61151,Installations techniques et machines,lu_2011_account_6115,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61152,61152,"Autres installations, outillages et machines",lu_2011_account_6115,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61153,61153,Matériel roulant,lu_2011_account_6115,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6116,6116,Malis sur emballages,lu_2011_account_611,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_612,612,"Sous-traitance, entretiens et réparations",lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6121,6121,"Sous-traitance générale (non incorporée directement aux ouvrages, travaux et produits)",lu_2011_account_612,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6122,6122,Entretien et réparations,lu_2011_account_612,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61221,61221,Sur installations techniques et machines,lu_2011_account_6122,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61222,61222,"Sur autres installations, outillages et machines",lu_2011_account_6122,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61223,61223,Sur matériel roulant,lu_2011_account_6122,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6123,6123,Contrats de maintenance,lu_2011_account_612,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6124,6124,Etudes et recherches (non incorporées dans les produits),lu_2011_account_612,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_613,613,Rémunérations d'intermédiaires et honoraires,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6131,6131,Commissions et courtages,lu_2011_account_613,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61311,61311,Commissions et courtages sur achats,lu_2011_account_6131,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61312,61312,Commissions et courtages sur ventes,lu_2011_account_6131,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61313,61313,Rémunérations des transitaires,lu_2011_account_6131,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6132,6132,Traitement informatique,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6133,6133,Services bancaires et assimilés,lu_2011_account_613,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61331,61331,"Frais sur titres (achat, vente, garde)",lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61332,61332,Commissions et frais sur émission d'emprunts,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61333,61333,Frais de compte,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61334,61334,Frais sur cartes de crédit,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61335,61335,Frais sur effets,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61336,61336,Rémunérations d'affacturage,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61337,61337,Location de coffres,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61338,61338,Autres frais et commissions bancaires (hors intérêts et frais assimilés),lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6134,6134,Honoraires,lu_2011_account_613,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61341,61341,Honoraires juridiques,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61342,61342,Honoraires comptables et d'audit,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61343,61343,Honoraires fiscaux,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61348,61348,Autres honoraires,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6135,6135,Frais d'actes et de contentieux,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6136,6136,Frais de recrutement de personnel,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6138,6138,Autres rémunérations d'intermédiaires et honoraires,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_614,614,Primes d'assurance,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6141,6141,Assurances sur biens de l'actif,lu_2011_account_614,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61411,61411,Bâtiments,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61412,61412,Véhicules,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61413,61413,Installations,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61418,61418,Sur autres biens de l'actif,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6142,6142,Assurances sur biens pris en location,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6143,6143,Assurance-transport,lu_2011_account_614,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61431,61431,sur achats,lu_2011_account_6143,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61432,61432,sur ventes,lu_2011_account_6143,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61438,61438,sur autres biens,lu_2011_account_6143,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6144,6144,Assurance risque d'exploitation,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6145,6145,Assurance insolvabilité clients,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6146,6146,Assurance responsabilité civile,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6148,6148,Autres assurances,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_615,615,Frais de marketing et de communication,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6151,6151,Frais de marketing et de publicité,lu_2011_account_615,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61511,61511,Annonces et insertions,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61512,61512,Echantillons,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61513,61513,Foires et expositions,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61514,61514,Cadeaux à la clientèle,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61515,61515,Catalogues et imprimés et publications,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61516,61516,Dons courants,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61517,61517,Sponsoring,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61518,61518,Autres achats de services publicitaires,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6152,6152,Frais de déplacements et de représentation,lu_2011_account_615,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61521,61521,Voyages et déplacements,lu_2011_account_6152,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_615211,615211,Direction (respectivement exploitant et associés),lu_2011_account_61521,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_615212,615212,Personnel,lu_2011_account_61521,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61522,61522,Frais de déménagement de l'entreprise,lu_2011_account_6152,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61523,61523,Missions,lu_2011_account_6152,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61524,61524,Réceptions et frais de représentation,lu_2011_account_6152,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6153,6153,Frais postaux et frais de télécommunications,lu_2011_account_615,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61531,61531,Timbres,lu_2011_account_6153,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61532,61532,Téléphone et autres frais de télécommunication,lu_2011_account_6153,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61538,61538,"Autres frais postaux (location de boîtes postales, etc.)",lu_2011_account_6153,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_616,616,Transports de biens et transports collectifs du personnel,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6161,6161,Transports sur achats,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6162,6162,Transports sur ventes,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6163,6163,Transports entre établissements ou chantiers,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6164,6164,Transports administratifs,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6165,6165,Transports collectifs du personnel,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6168,6168,Autres transports,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_617,617,Personnel extérieur à l'entreprise,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6171,6171,Personnel intérimaire,lu_2011_account_617,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6172,6172,Personnel prêté à l'entreprise,lu_2011_account_617,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_618,618,Charges externes diverses,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6181,6181,Documentation,lu_2011_account_618,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61811,61811,Documentation générale,lu_2011_account_6181,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_61812,61812,Documentation technique,lu_2011_account_6181,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6182,6182,"Frais de colloques, séminaires, conférences",lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6183,6183,Elimination des déchets industriels,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6184,6184,Elimination de déchets non industriels,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6185,6185,Evacuation des eaux usées,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6186,6186,Frais de surveillance,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6187,6187,Cotisations aux associations professionnelles,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6188,6188,Autres charges externes diverses,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_619,619,"Rabais, remises et ristournes obtenus sur autres charges externes",lu_2011_account_61,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62,62,Frais de personnel,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_621,621,Rémunérations des salariés,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_123,account_financial_report_abr_123" -lu_2011_account_6211,6211,Salaires bruts,lu_2011_account_621,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62111,62111,Salaires de base,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62112,62112,Suppléments pour travail,lu_2011_account_6211,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_621121,621121,Dimanche,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_621122,621122,Jours fériés légaux,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_621123,621123,Heures supplémentaires,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_621128,621128,Autres suppléments,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62113,62113,Primes de ménage,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62114,62114,"Gratifications, primes et commissions",lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62115,62115,Avantages en nature,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62116,62116,Indemnités de licenciement,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62117,62117,Trimestre de faveur,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6218,6218,Autres avantages,lu_2011_account_621,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6219,6219,Remboursements sur salaires,lu_2011_account_621,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62191,62191,Remboursements mutualité,lu_2011_account_6219,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62192,62192,"Remboursements pour congé politique, sportif, culturel, éducatif et mandats sociaux",lu_2011_account_6219,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62193,62193,Remboursements trimestre de faveur,lu_2011_account_6219,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_622,622,Autre personnel,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_123,account_financial_report_abr_123" -lu_2011_account_6221,6221,Etudiants,lu_2011_account_622,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6222,6222,Salaires occasionnels,lu_2011_account_622,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6228,6228,Autre personnel temporaire,lu_2011_account_622,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_623,623,Charges sociales (part patronale),lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_124,account_financial_report_abr_124" -lu_2011_account_6231,6231,Charges sociales salariés,lu_2011_account_623,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62311,62311,Caisse Nationale de Santé,lu_2011_account_6231,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62312,62312,Caisse Nationale d'Assurance-Pension,lu_2011_account_6231,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_62318,62318,Cotisations patronales complémentaires,lu_2011_account_6231,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6232,6232,Assurance accidents du travail,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6233,6233,Service de santé au travail,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6238,6238,Autres charges sociales patronales,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6239,6239,Remboursements de charges sociales,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_624,624,Pensions complémentaires,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_125,account_financial_report_abr_125" -lu_2011_account_6241,6241,Primes à des fonds de pensions extérieurs,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6242,6242,Dotation aux provisions pour pensions complémentaires,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6243,6243,Retenue d'impôt sur pension complémentaire,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6244,6244,Prime d'assurance insolvabilité,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6245,6245,Pensions complémentaires versées par l'employeur,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_628,628,Autres charges sociales,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_126,account_financial_report_abr_126" -lu_2011_account_6281,6281,Médecine du travail,lu_2011_account_628,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6288,6288,Autres charges sociales diverses,lu_2011_account_628,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_63,63,Dotations aux corrections de valeur des éléments d'actif non financiers,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_631,631,Dotations aux corrections de valeur sur frais d'établissement et frais assimilés,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_128,account_financial_report_abr_128" -lu_2011_account_6311,6311,Frais de constitution,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6312,6312,Frais de premier établissement,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6313,6313,Frais d'augmentation de capital et d'opérations diverses,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6314,6314,Frais d'émission d'emprunts,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6318,6318,Autres frais assimilés,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_632,632,Dotations aux corrections de valeur sur immobilisations incorporelles,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_128,account_financial_report_abr_128" -lu_2011_account_6321,6321,Frais de recherche et de développement,lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6322,6322,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6323,6323,Fonds de commerce dans la mesure où il a été acquis à titre onéreux,lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6324,6324,Acomptes versés et immobilisations incorporelles en cours,lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_633,633,Dotations aux corrections de valeur sur immobilisations corporelles,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_128,account_financial_report_abr_128" -lu_2011_account_6331,6331,Terrains et constructions,lu_2011_account_633,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_63311,63311,Terrains,lu_2011_account_6331,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_63312,63312,Agencements et aménagements de terrains,lu_2011_account_6331,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_63313,63313,Constructions,lu_2011_account_6331,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6332,6332,Installations techniques et machines,lu_2011_account_633,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6333,6333,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_633,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6334,6334,Acomptes versés et immobilisations corporelles en cours,lu_2011_account_633,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_634,634,Dotations aux corrections de valeur sur stocks,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_129,account_financial_report_abr_129" -lu_2011_account_6341,6341,Matières premières et consommables,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6342,6342,Produits en cours de fabrication et commandes en cours,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6343,6343,Produits finis et marchandises,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6344,6344,Terrains et immeubles destinés à la revente,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6345,6345,Acomptes versés,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_635,635,Dotations aux corrections de valeur sur créances de l'actif circulant,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_129,account_financial_report_abr_129" -lu_2011_account_6351,6351,Créances résultant de ventes et prestations de services,lu_2011_account_635,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6352,6352,Créances sur des entreprises liées et des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_635,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6353,6353,Autres créances,lu_2011_account_635,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64,64,Autres charges d'exploitation,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f,"account_financial_report_130,account_financial_report_abr_130" -lu_2011_account_641,641,"Redevances pour concessions, brevets, licences, marques, droits et valeurs similaires",lu_2011_account_64,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6411,6411,Concessions,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6412,6412,Brevets,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6413,6413,Licences informatiques,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6414,6414,Marques et franchises,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6415,6415,Droits et valeurs similaires,lu_2011_account_641,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64151,64151,Droits d'auteur et de reproduction,lu_2011_account_6415,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64158,64158,Autres droits et valeurs similaires,lu_2011_account_6415,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_642,642,Indemnités,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_643,643,Jetons de présence,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_644,644,Tantièmes,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_645,645,Pertes sur créances irrécouvrables,lu_2011_account_64,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6451,6451,Créances résultant de ventes et de prestations de services,lu_2011_account_645,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6452,6452,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_645,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6453,6453,Autres créances,lu_2011_account_645,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_646,646,"Impôts, taxes et versements assimilés",lu_2011_account_64,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6461,6461,Impôt foncier,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6462,6462,TVA non déductible,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6463,6463,Droits sur les marchandises en provenance de l'étranger,lu_2011_account_646,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64631,64631,Droits d'accises et taxe de consommation sur marchandises en provenance de l'étranger,lu_2011_account_6463,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64632,64632,Droits de douane,lu_2011_account_6463,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64633,64633,Montants compensatoires,lu_2011_account_6463,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6464,6464,Droits d'accises à la production et taxe de consommation,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6465,6465,"Droits d'enregistrement et de timbre, droits d'hypothèques",lu_2011_account_646,view,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64651,64651,Droits d'enregistrement,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64652,64652,Taxe d'abonnement,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64653,64653,Droits d'hypothèques,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64654,64654,Droits de timbre,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_64658,64658,"Autres droits d'enregistrement et de timbre, droits d'hypothèques",lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6466,6466,Taxes sur les véhicules,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6467,6467,Taxe de cabaretage,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6468,6468,Autres droits et impôts,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_6469,6469,Dotations aux provisions pour impôts,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_647,647,Dotations aux plus-values immunisées,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_648,648,Autres charges d'exploitation diverses,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_649,649,Dotations aux provisions d'exploitation,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, -lu_2011_account_65,65,Charges financières,lu_2011_account_6,view,account_type_2011_6_charge_finance,f, -lu_2011_account_651,651,Dotations aux corrections de valeur et ajustements pour juste valeur sur immobilisations financières,lu_2011_account_65,view,account_type_2011_6_charge_finance,f,"account_financial_report_131,account_financial_report_abr_131" -lu_2011_account_6511,6511,Dotations aux corrections de valeur sur immobilisations financières,lu_2011_account_651,view,account_type_2011_6_charge_finance,f, -lu_2011_account_65111,65111,Parts dans des entreprises liées,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65112,65112,Créances sur des entreprises liées,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65113,65113,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65114,65114,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65115,65115,Titres ayant le caractère d'immobilisations,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65116,65116,Prêts et créances immobilisées,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65117,65117,Actions propres ou parts propres,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6512,6512,Ajustements pour juste valeur sur immobilisations financières,lu_2011_account_651,other,account_type_2011_6_charge_finance,f, -lu_2011_account_653,653,Dotations aux corrections de valeur et ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_65,view,account_type_2011_6_charge_finance,f,"account_financial_report_132,account_financial_report_abr_132" -lu_2011_account_6531,6531,Dotations aux corrections de valeur sur valeurs mobilières,lu_2011_account_653,view,account_type_2011_6_charge_finance,f, -lu_2011_account_65311,65311,Parts dans des entreprises liées,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65312,65312,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65313,65313,Actions propres ou parts propres,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65318,65318,Autres valeurs mobilières,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6532,6532,Dotations aux corrections de valeur sur créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_653,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6533,6533,Dotations aux corrections de valeur sur autres créances,lu_2011_account_653,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6534,6534,Ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_653,other,account_type_2011_6_charge_finance,f, -lu_2011_account_654,654,Moins-values de cession de valeurs mobilières,lu_2011_account_65,view,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_6541,6541,Parts dans des entreprises liées,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6542,6542,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6543,6543,Actions propres ou parts propres,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6548,6548,Autres valeurs mobilières,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, -lu_2011_account_655,655,Intérêts et escomptes,lu_2011_account_65,view,account_type_2011_6_charge_finance,f, -lu_2011_account_6551,6551,Intérêts des dettes financières,lu_2011_account_655,view,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_65511,65511,Intérêts des dettes subordonnées,lu_2011_account_6551,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65512,65512,Intérêts des emprunts obligataires,lu_2011_account_6551,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6552,6552,Intérêts bancaires et assimilés,lu_2011_account_655,view,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_65521,65521,Intérêts bancaires sur comptes courants,lu_2011_account_6552,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65522,65522,Intérêts bancaires sur opérations de financement,lu_2011_account_6552,other,account_type_2011_6_charge_finance,f, -lu_2011_account_65523,65523,Intérêts sur leasings financiers,lu_2011_account_6552,other,account_type_2011_6_charge_finance,f, -lu_2011_account_6553,6553,Intérêts sur dettes commerciales,lu_2011_account_655,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_6554,6554,Intérêts sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_655,other,account_type_2011_6_charge_finance,f,"account_financial_report_134,account_financial_report_abr_134" -lu_2011_account_6555,6555,Escomptes et frais sur effets,lu_2011_account_655,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_6556,6556,Escomptes accordés,lu_2011_account_655,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_6558,6558,Intérêts sur autres emprunts et dettes,lu_2011_account_655,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_656,656,Pertes de change,lu_2011_account_65,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_657,657,Quote-part de perte dans les entreprises collectives (autres que les sociétés de capitaux),lu_2011_account_65,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_658,658,Autres charges financières,lu_2011_account_65,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_659,659,Dotations aux provisions financières,lu_2011_account_65,other,account_type_2011_6_charge_finance,f,"account_financial_report_135,account_financial_report_abr_135" -lu_2011_account_66,66,Charges exceptionnelles,lu_2011_account_6,view,account_type_2011_6_charge_exception,f,"account_financial_report_136,account_financial_report_abr_136" -lu_2011_account_661,661,Dotations aux corrections de valeur exceptionnelles sur immobilisations incorporelles et corporelles,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, -lu_2011_account_6611,6611,Sur immobilisations incorporelles,lu_2011_account_661,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6612,6612,Sur immobilisations corporelles,lu_2011_account_661,other,account_type_2011_6_charge_exception,f, -lu_2011_account_662,662,Dotations aux corrections de valeur exceptionnelles sur éléments de l'actif circulant,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, -lu_2011_account_6621,6621,Sur stocks,lu_2011_account_662,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6622,6622,Sur créances,lu_2011_account_662,other,account_type_2011_6_charge_exception,f, -lu_2011_account_663,663,Valeur comptable des immobilisations incorporelles et corporelles cédées,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, -lu_2011_account_6631,6631,Immobilisations incorporelles,lu_2011_account_663,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6632,6632,Immobilisations corporelles,lu_2011_account_663,other,account_type_2011_6_charge_exception,f, -lu_2011_account_664,664,Valeur comptable des immobilisations financières cédées,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, -lu_2011_account_6641,6641,Parts dans des entreprises liées,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6642,6642,Créances sur des entreprises liées,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6643,6643,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6644,6644,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6645,6645,Titres ayant le caractère d'immobilisations,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6646,6646,Prêts et créances immobilisées,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6647,6647,Actions propres ou parts propres,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, -lu_2011_account_665,665,Valeur comptable des créances de l'actif circulant financier cédées,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, -lu_2011_account_6651,6651,Sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_665,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6652,6652,Sur autres créances,lu_2011_account_665,other,account_type_2011_6_charge_exception,f, -lu_2011_account_668,668,Autres charges exceptionnelles,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, -lu_2011_account_6681,6681,Pénalités sur marchés et dédits payés sur achats et ventes,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6682,6682,"Amendes et pénalités fiscales, sociales et pénales",lu_2011_account_668,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6683,6683,Dommages et intérêts,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6684,6684,Malis provenant de clauses d'indexation,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, -lu_2011_account_6688,6688,Autres charges exceptionnelles diverses,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, -lu_2011_account_669,669,Dotations aux provisions exceptionnelles,lu_2011_account_66,other,account_type_2011_6_charge_exception,f, -lu_2011_account_67,67,Impôts sur le résultat,lu_2011_account_6,view,account_type_2011_6_charge_impot,f,"account_financial_report_137,account_financial_report_abr_137" -lu_2011_account_671,671,Impôt sur le revenu des collectivités,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, -lu_2011_account_6711,6711,Exercice courant,lu_2011_account_671,other,account_type_2011_6_charge_impot,f, -lu_2011_account_6712,6712,Exercices antérieurs,lu_2011_account_671,other,account_type_2011_6_charge_impot,f, -lu_2011_account_672,672,Impôt commercial,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, -lu_2011_account_6721,6721,Exercice courant,lu_2011_account_672,other,account_type_2011_6_charge_impot,f, -lu_2011_account_6722,6722,Exercices antérieurs,lu_2011_account_672,other,account_type_2011_6_charge_impot,f, -lu_2011_account_673,673,Impôts étrangers sur le résultat,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, -lu_2011_account_6731,6731,Retenues d'impôt à la source,lu_2011_account_673,other,account_type_2011_6_charge_impot,f, -lu_2011_account_6732,6732,Impôts supportés par les établissements stables,lu_2011_account_673,view,account_type_2011_6_charge_impot,f, -lu_2011_account_67321,67321,Exercice courant,lu_2011_account_6732,other,account_type_2011_6_charge_impot,f, -lu_2011_account_67322,67322,Exercices antérieurs,lu_2011_account_6732,other,account_type_2011_6_charge_impot,f, -lu_2011_account_6733,6733,Impôts supportés par les entreprises non résidentes,lu_2011_account_673,other,account_type_2011_6_charge_impot,f, -lu_2011_account_6738,6738,Autres impôts étrangers,lu_2011_account_673,other,account_type_2011_6_charge_impot,f, -lu_2011_account_679,679,Dotations aux provisions pour impôts sur le résultat,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, -lu_2011_account_6791,6791,Dotations aux provisions pour impôts,lu_2011_account_679,other,account_type_2011_6_charge_impot,f, -lu_2011_account_6792,6792,Dotations aux provisions pour impôts différés,lu_2011_account_679,other,account_type_2011_6_charge_impot,f, -lu_2011_account_68,68,Autres impôts ne figurant pas sous le poste ci-dessus,lu_2011_account_6,view,account_type_2011_6_charge_impot,f,"account_financial_report_138,account_financial_report_abr_138" -lu_2011_account_681,681,Impôt sur la fortune,lu_2011_account_68,view,account_type_2011_6_charge_impot,f, -lu_2011_account_6811,6811,Exercice courant,lu_2011_account_681,other,account_type_2011_6_charge_impot,f, -lu_2011_account_6812,6812,Exercices antérieurs,lu_2011_account_681,other,account_type_2011_6_charge_impot,f, -lu_2011_account_682,682,Taxe d'abonnement,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, -lu_2011_account_683,683,Impôts étrangers,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, -lu_2011_account_688,688,Autres impôts et taxes,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, -lu_2011_account_689,689,Dotations aux provisions pour autres impôts,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, -lu_2011_account_7,7,CLASSE 7 - COMPTES DE PRODUITS,lu_2011_account_resultat,view,account.data_account_type_view,f, -lu_2011_account_70,70,Montant net du chiffre d'affaires,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_141,account_financial_report_abr_141,account_financial_report_abr_120" -lu_2011_account_701,701,Ventes sur commandes en cours,lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7011,7011,Produits,lu_2011_account_701,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7012,7012,Prestations de services,lu_2011_account_701,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7013,7013,Immeubles en construction,lu_2011_account_701,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_702,702,Ventes de produits finis,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_703,703,Ventes de produits intermédiaires,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_704,704,Ventes de produits résiduels,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_705,705,Ventes d'éléments destinés à la revente,lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7051,7051,Ventes de marchandises,lu_2011_account_705,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7052,7052,Ventes de terrains et d'immeubles existants (promotion immobilière),lu_2011_account_705,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7053,7053,Ventes d'autres éléments destinés à la revente,lu_2011_account_705,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_706,706,Prestations de services,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_708,708,Autres éléments du chiffre d'affaires,lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7081,7081,Commissions et courtages,lu_2011_account_708,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7082,7082,Locations,lu_2011_account_708,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_70821,70821,Loyer immobilier,lu_2011_account_7082,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_70822,70822,Loyer mobilier,lu_2011_account_7082,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7083,7083,Ventes d'emballages,lu_2011_account_708,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7088,7088,Autres éléments divers du chiffre d'affaires,lu_2011_account_708,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_709,709,"Rabais, remises et ristournes accordés par l'entreprise",lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7091,7091,Sur ventes sur commandes en cours,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7092,7092,Sur ventes de produits finis,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7093,7093,Sur ventes de produits intermédiaires,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7094,7094,Sur ventes de produits résiduels,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7095,7095,Sur ventes d'éléments destinés à la revente,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7096,7096,Sur prestations de services,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7098,7098,Sur autres éléments du chiffre d'affaires,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_71,71,"Variation des stocks de produits finis, d'en cours de fabrication et des commandes en cours",lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_142,account_financial_report_abr_141,account_financial_report_abr_120" -lu_2011_account_711,711,Variation des stocks de produits en cours de fabrication et de commandes en cours,lu_2011_account_71,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7111,7111,Variation des stocks de produits en cours,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7112,7112,Variation des stocks de commandes en cours – produits,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7113,7113,Variation des stocks de commandes en cours – prestations de services,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7114,7114,Variation des stocks d'immeubles en construction,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_712,712,Variation des stocks de produits finis et marchandises,lu_2011_account_71,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7121,7121,Variation des stocks de produits finis,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7122,7122,Variation des stocks de produits intermédiaires,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7123,7123,Variation des stocks de produits résiduels,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7126,7126,Variation des stocks de marchandises,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7127,7127,"Variation des stocks de marchandises en voie d'acheminement, mises en dépôt ou données en consignation",lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_72,72,Production immobilisée,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_143,account_financial_report_abr_141,account_financial_report_abr_120" -lu_2011_account_721,721,Immobilisations incorporelles,lu_2011_account_72,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7211,7211,Frais de recherche et développement,lu_2011_account_721,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7212,7212,"Concessions, brevets, licences, marques, droits et valeurs similaires",lu_2011_account_721,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_72121,72121,Concessions,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_72122,72122,Brevets,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_72123,72123,Licences informatiques,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_72124,72124,Marques et franchises,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_72125,72125,Droits et valeurs similaires,lu_2011_account_7212,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_721251,721251,Droits d'auteur et de reproduction,lu_2011_account_72125,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_721258,721258,Autres droits et valeurs similaires,lu_2011_account_72125,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_722,722,Immobilisations corporelles,lu_2011_account_72,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7221,7221,Terrains et constructions,lu_2011_account_722,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7222,7222,Installations techniques et machines,lu_2011_account_722,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7223,7223,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_722,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_73,73,Reprises de corrections de valeur des éléments d'actif non financiers,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_abr_141,account_financial_report_abr_120" -lu_2011_account_732,732,Reprises de corrections de valeur sur immobilisations incorporelles,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_145" -lu_2011_account_7321,7321,Frais de recherche et de développement,lu_2011_account_732,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7322,7322,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_732,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7323,7323,"Fonds de commerce, dans la mesure où il a été acquis à titre onéreux",lu_2011_account_732,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7324,7324,Acomptes versés et immobilisations incorporelles en cours,lu_2011_account_732,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_733,733,Reprises de corrections de valeur sur immobilisations corporelles,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_145" -lu_2011_account_7331,7331,Terrains et constructions,lu_2011_account_733,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_73311,73311,Terrains,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_73312,73312,Agencements et aménagements de terrains,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_73313,73313,Constructions,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_73314,73314,Constructions sur sol d'autrui,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7332,7332,Installations techniques et machines,lu_2011_account_733,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7333,7333,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_733,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7334,7334,Acomptes versés et immobilisations corporelles en cours,lu_2011_account_733,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_734,734,Reprises de corrections de valeur sur stocks,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_146" -lu_2011_account_7341,7341,Matières premières et consommables,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7342,7342,Produits en cours de fabrication et commandes en cours,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7343,7343,Produits finis et marchandises,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7344,7344,Terrains et immeubles destinés à la revente,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7345,7345,Acomptes versés,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_735,735,Reprises de corrections de valeur sur créances de l'actif circulant,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_146" -lu_2011_account_7351,7351,Créances résultant de ventes et prestations de services,lu_2011_account_735,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7352,7352,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_735,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7353,7353,Autres créances,lu_2011_account_735,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_74,74,Autres produits d'exploitation,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,"account_financial_report_147,account_financial_report_abr_147" -lu_2011_account_741,741,"Redevances pour concessions, brevets, licences, marques, droits et valeurs similaires",lu_2011_account_74,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7411,7411,Concessions,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7412,7412,Brevets,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7413,7413,Licences informatiques,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7414,7414,Marques et franchises,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7415,7415,Droits et valeurs similaires,lu_2011_account_741,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_74151,74151,Droits d'auteur et de reproduction,lu_2011_account_7415,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_74158,74158,Autres droits et valeurs similaires,lu_2011_account_7415,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_742,742,Revenus des immeubles non affectés aux activités professionnelles,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_743,743,"Jetons de présence, tantièmes et rémunérations assimilées",lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_744,744,Subventions d'exploitation,lu_2011_account_74,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7441,7441,Subventions sur produits,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7442,7442,Bonifications d'intérêt,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7443,7443,Montants compensatoires,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7444,7444,Subventions destinées à promouvoir l'emploi,lu_2011_account_744,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_74441,74441,Primes d'apprentissage reçues,lu_2011_account_7444,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_74442,74442,Autres subventions destinées à promouvoir l'emploi,lu_2011_account_7444,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7448,7448,Autres subventions d'exploitation,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_745,745,Ristournes perçues des coopératives (provenant des excédents),lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_746,746,Indemnités d'assurance touchées,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_747,747,Reprises de plus-values immunisées et de subventions d'investissement en capital,lu_2011_account_74,view,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7471,7471,Plus-values immunisées non réinvesties,lu_2011_account_747,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7472,7472,Plus-values immunisées réinvesties,lu_2011_account_747,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_7473,7473,Subventions d'investissement en capital,lu_2011_account_747,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_748,748,Autres produits d'exploitation divers,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_749,749,Reprises sur provisions d'exploitation,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, -lu_2011_account_75,75,Produits financiers,lu_2011_account_7,view,account_type_2011_7_produit_financier,f, -lu_2011_account_751,751,Reprises sur corrections de valeur et ajustements pour juste valeur sur immobilisations financières,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, -lu_2011_account_7511,7511,Reprises sur corrections de valeur sur immobilisations financières,lu_2011_account_751,view,account_type_2011_7_produit_financier,f, -lu_2011_account_75111,75111,Parts dans des entreprises liées,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,"account_financial_report_149,account_financial_report_abr_149" -lu_2011_account_75112,75112,Créances sur des entreprises liées,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,"account_financial_report_149,account_financial_report_abr_149" -lu_2011_account_75113,75113,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_75114,75114,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_75115,75115,Titres ayant le caractère d'immobilisations,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_75116,75116,Prêts et créances immobilisées,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_75117,75117,Actions propres ou parts propres,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_7512,7512,Ajustements pour juste valeur sur immobilisations financières,lu_2011_account_751,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_752,752,Revenus des immobilisations financières,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, -lu_2011_account_7521,7521,Parts dans des entreprises liées,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,"account_financial_report_149,account_financial_report_abr_149" -lu_2011_account_7522,7522,Créances sur des entreprises liées,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,"account_financial_report_149,account_financial_report_abr_149" -lu_2011_account_7523,7523,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_7524,7524,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_7525,7525,Titres ayant le caractère d'immobilisations,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_7526,7526,Prêts et créances immobilisées,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_7527,7527,Actions propres ou parts propres,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,"account_financial_report_150,account_financial_report_abr_150" -lu_2011_account_753,753,Reprises sur corrections de valeur et ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, -lu_2011_account_7531,7531,Reprises sur corrections de valeur sur créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_753,other,account_type_2011_7_produit_financier,f,"account_financial_report_152,account_financial_report_abr_152" -lu_2011_account_7532,7532,Reprises sur corrections de valeur sur autres créances,lu_2011_account_753,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_7533,7533,Reprises sur corrections de valeur sur valeurs mobilières,lu_2011_account_753,view,account_type_2011_7_produit_financier,f, -lu_2011_account_75331,75331,Parts dans des entreprises liées,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,"account_financial_report_152,account_financial_report_abr_152" -lu_2011_account_75332,75332,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_75333,75333,Actions propres ou parts propres,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_75338,75338,Autres valeurs mobilières,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_7534,7534,Ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_753,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_754,754,Plus-value de cession et autres produits de valeurs mobilières,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, -lu_2011_account_7541,7541,Plus-value de cession de valeurs mobilières,lu_2011_account_754,view,account_type_2011_7_produit_financier,f, -lu_2011_account_75411,75411,Parts dans des entreprises liées,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,"account_financial_report_152,account_financial_report_abr_152" -lu_2011_account_75412,75412,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_75413,75413,Actions propres ou parts propres,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_75418,75418,Autres valeurs mobilières,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_7548,7548,Autres produits de valeurs mobilières,lu_2011_account_754,view,account_type_2011_7_produit_financier,f, -lu_2011_account_75481,75481,Parts dans des entreprises liées,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,"account_financial_report_152,account_financial_report_abr_152" -lu_2011_account_75482,75482,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_75483,75483,Actions propres ou parts propres,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_75488,75488,Autres valeurs mobilières,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,"account_financial_report_153,account_financial_report_abr_153" -lu_2011_account_755,755,Autres intérêts et escomptes,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, -lu_2011_account_7552,7552,Intérêts bancaires et assimilés,lu_2011_account_755,view,account_type_2011_7_produit_financier,f,"account_financial_report_156,account_financial_report_abr_156" -lu_2011_account_75521,75521,Intérêts sur comptes courants,lu_2011_account_7552,other,account_type_2011_7_produit_financier,f, -lu_2011_account_75522,75522,Intérêts sur comptes à terme,lu_2011_account_7552,other,account_type_2011_7_produit_financier,f, -lu_2011_account_75523,75523,Intérêts sur leasings financiers,lu_2011_account_7552,other,account_type_2011_7_produit_financier,f, -lu_2011_account_7553,7553,Intérêts sur créances commerciales,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,"account_financial_report_156,account_financial_report_abr_156" -lu_2011_account_7554,7554,Intérêts sur des entreprises liées et des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,"account_financial_report_155,account_financial_report_abr_155" -lu_2011_account_7555,7555,Escomptes d'effets de commerce,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,"account_financial_report_156,account_financial_report_abr_156" -lu_2011_account_7556,7556,Escomptes obtenus,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,"account_financial_report_156,account_financial_report_abr_156" -lu_2011_account_7558,7558,Intérêts sur autres créances,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,"account_financial_report_156,account_financial_report_abr_156" -lu_2011_account_756,756,Gains de change,lu_2011_account_75,other,account_type_2011_7_produit_financier,f, -lu_2011_account_757,757,Quote-part de bénéfice dans les entreprises collectives (autres que les sociétés de capitaux),lu_2011_account_75,other,account_type_2011_7_produit_financier,f, -lu_2011_account_758,758,Autres produits financiers,lu_2011_account_75,other,account_type_2011_7_produit_financier,f,"account_financial_report_156,account_financial_report_abr_156" -lu_2011_account_759,759,Reprises sur provisions financières,lu_2011_account_75,other,account_type_2011_7_produit_financier,f, -lu_2011_account_76,76,Produits exceptionnels,lu_2011_account_7,view,account_type_2011_7_produit_exceptionnel,f,"account_financial_report_157,account_financial_report_abr_157" -lu_2011_account_761,761,Reprises sur corrections de valeur exceptionnelles sur immobilisations incorporelles et corporelles,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7611,7611,Immobilisations incorporelles,lu_2011_account_761,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7612,7612,Immobilisations corporelles,lu_2011_account_761,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_762,762,Reprises sur corrections de valeur exceptionnelles sur éléments de l'actif circulant,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7621,7621,Sur stocks,lu_2011_account_762,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7622,7622,Sur créances de l'actif circulant,lu_2011_account_762,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_763,763,Produits de cession d'immobilisations incorporelles et corporelles,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7631,7631,Immobilisations incorporelles,lu_2011_account_763,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7632,7632,Immobilisations corporelles,lu_2011_account_763,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_764,764,Produits de cession d'immobilisations financières,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7641,7641,Parts dans des entreprises liées,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7642,7642,Créances sur des entreprises liées,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7643,7643,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7644,7644,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7645,7645,Titres ayant le caractère d'immobilisations,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7646,7646,Prêts et créances immobilisés,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7647,7647,Actions propres ou parts propres,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_765,765,Produits de cession sur créances de l'actif circulant financier,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7651,7651,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_765,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7652,7652,Autres créances,lu_2011_account_765,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_768,768,Autres produits exceptionnels,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7681,7681,Pénalités sur marchés et dédits perçus sur achats et sur ventes,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7682,7682,Libéralités reçues,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7683,7683,Rentrées sur créances amorties,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7684,7684,Subventions exceptionnelles,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7685,7685,Bonis provenant de clauses d'indexation,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7686,7686,Bonis provenant du rachat par l'entreprise d'actions et d'obligations émises par elle-même,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7688,7688,Autres produits exceptionnels divers,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_769,769,Reprises sur provisions exceptionnelles,lu_2011_account_76,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_77,77,Régularisations d'impôts sur le résultat,lu_2011_account_7,view,account_type_2011_7_produit_exceptionnel,f,"account_financial_report_137,account_financial_report_abr_137" -lu_2011_account_771,771,Régularisations d'impôt sur le revenu des collectivités,lu_2011_account_77,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_772,772,Régularisations d'impôt commercial,lu_2011_account_77,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_773,773,Régularisations d'impôts étrangers sur le résultat,lu_2011_account_77,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_779,779,Reprises sur provisions pour impôts sur le résultat,lu_2011_account_77,view,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7791,7791,Reprises sur provisions pour impôts,lu_2011_account_779,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_7792,7792,Reprises sur provisions pour impôts différés,lu_2011_account_779,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_78,78,Régularisations d'autres impôts ne figurant pas sous le poste ci-dessus,lu_2011_account_7,view,account_type_2011_7_produit_exceptionnel,f,"account_financial_report_138,account_financial_report_abr_138" -lu_2011_account_781,781,Régularisations d'impôt sur la fortune,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_782,782,Régularisations de taxes d'abonnement,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_783,783,Régularisations d'impôts étrangers,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_788,788,Régularisations d'autres impôts et taxes,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, -lu_2011_account_789,789,Reprises sur provisions pour autres impôts,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, +id,code,name,parent_id/id,type,user_type/id,reconcile,financial_report_ids/id +lu_2011_account_0,0,Plan de compte Luxembourgeois - Loi de Juin 2009 (THAMINI & ADN & ACSONE),,view,account.data_account_type_view,f, +lu_2011_account_bilan,bilan,TOTAL CLASSES 1 A 5,lu_2011_account_0,view,account.data_account_type_view,f, +lu_2011_account_1,1,"CLASSE 1 - COMPTES DE CAPITAUX, DE PROVISIONS ET DE DETTES FINANCIERES",lu_2011_account_bilan,view,account_type_2011_1_capital,f, +lu_2011_account_10,10,Capital ou dotation des succursales et comptes de l'exploitant,lu_2011_account_1,view,account_type_2011_1_capital,f, +lu_2011_account_101,101,Capital souscrit (Sociétés de capitaux - Montant total),lu_2011_account_10,other,account_type_2011_1_capital,f,account_financial_report_68 +lu_2011_account_102,102,Capital souscrit non appelé (Sociétés de capitaux),lu_2011_account_10,other,account_type_2011_1_capital,f,account_financial_report_17 +lu_2011_account_103,103,Capital souscrit appelé et non versé (Sociétés de capitaux),lu_2011_account_10,other,account_type_2011_1_capital,f,account_financial_report_18 +lu_2011_account_104,104,Capital des entreprises commerçants personnes physiques et des sociétés de personnes,lu_2011_account_10,view,account_type_2011_1_capital,f,account_financial_report_68 +lu_2011_account_1041,1041,Commerçants personnes physiques,lu_2011_account_104,other,account_type_2011_1_capital,f, +lu_2011_account_1042,1042,Sociétés de personnes,lu_2011_account_104,other,account_type_2011_1_capital,f, +lu_2011_account_105,105,Dotation des succursales,lu_2011_account_10,other,account_type_2011_1_capital,f,account_financial_report_68 +lu_2011_account_106,106,Comptes de l'exploitant ou des coexploitants,lu_2011_account_10,view,account_type_2011_1_capital,f,account_financial_report_68 +lu_2011_account_1061,1061,Prélèvements privés de l'exploitant ou des coexploitants,lu_2011_account_106,view,account_type_2011_1_capital,f, +lu_2011_account_10611,10611,Prélèvements en numéraire (train de vie),lu_2011_account_1061,other,account_type_2011_1_capital,f, +lu_2011_account_10612,10612,"Prélèvements en nature de marchandises, de produits finis et services (au prix de revient)",lu_2011_account_1061,other,account_type_2011_1_capital,f, +lu_2011_account_10613,10613,Part personnelle des frais de maladie,lu_2011_account_1061,other,account_type_2011_1_capital,f, +lu_2011_account_10614,10614,Primes d'assurances privées,lu_2011_account_1061,view,account_type_2011_1_capital,f, +lu_2011_account_106141,106141,Vie,lu_2011_account_10614,other,account_type_2011_1_capital,f, +lu_2011_account_106142,106142,Accident,lu_2011_account_10614,other,account_type_2011_1_capital,f, +lu_2011_account_106143,106143,Incendie,lu_2011_account_10614,other,account_type_2011_1_capital,f, +lu_2011_account_106144,106144,Responsabilité civile,lu_2011_account_10614,other,account_type_2011_1_capital,f, +lu_2011_account_106145,106145,Multirisques,lu_2011_account_10614,other,account_type_2011_1_capital,f, +lu_2011_account_106148,106148,Autres primes d'assurances privées,lu_2011_account_10614,other,account_type_2011_1_capital,f, +lu_2011_account_10615,10615,Cotisations,lu_2011_account_1061,view,account_type_2011_1_capital,f, +lu_2011_account_106151,106151,Assurances sociales (assurance dépendance),lu_2011_account_10615,other,account_type_2011_1_capital,f, +lu_2011_account_106152,106152,Allocations familiales,lu_2011_account_10615,other,account_type_2011_1_capital,f, +lu_2011_account_106153,106153,Cotisations pour mutuelles,lu_2011_account_10615,other,account_type_2011_1_capital,f, +lu_2011_account_106154,106154,"Caisse de décès, médico-chirurgicale, Prestaplus",lu_2011_account_10615,other,account_type_2011_1_capital,f, +lu_2011_account_106158,106158,Autres cotisations,lu_2011_account_10615,other,account_type_2011_1_capital,f, +lu_2011_account_10616,10616,Prélèvements en nature (quote-part privée dans les frais généraux),lu_2011_account_1061,view,account_type_2011_1_capital,f, +lu_2011_account_106161,106161,Salaires,lu_2011_account_10616,other,account_type_2011_1_capital,f, +lu_2011_account_106162,106162,Loyer,lu_2011_account_10616,other,account_type_2011_1_capital,f, +lu_2011_account_106163,106163,"Chauffage, gaz, électricité",lu_2011_account_10616,other,account_type_2011_1_capital,f, +lu_2011_account_106164,106164,Eau,lu_2011_account_10616,other,account_type_2011_1_capital,f, +lu_2011_account_106165,106165,Téléphone,lu_2011_account_10616,other,account_type_2011_1_capital,f, +lu_2011_account_106166,106166,Voiture,lu_2011_account_10616,other,account_type_2011_1_capital,f, +lu_2011_account_106168,106168,Autres prélèvements en nature,lu_2011_account_10616,other,account_type_2011_1_capital,f, +lu_2011_account_10617,10617,Acquisitions,lu_2011_account_1061,view,account_type_2011_1_capital,f, +lu_2011_account_106171,106171,Mobilier privé,lu_2011_account_10617,other,account_type_2011_1_capital,f, +lu_2011_account_106172,106172,Voiture privée,lu_2011_account_10617,other,account_type_2011_1_capital,f, +lu_2011_account_106173,106173,Titres privés,lu_2011_account_10617,other,account_type_2011_1_capital,f, +lu_2011_account_106174,106174,Immeubles privés,lu_2011_account_10617,other,account_type_2011_1_capital,f, +lu_2011_account_106178,106178,Autres acquisitions,lu_2011_account_10617,other,account_type_2011_1_capital,f, +lu_2011_account_10618,10618,Impôts,lu_2011_account_1061,view,account_type_2011_1_capital,f, +lu_2011_account_106181,106181,Impôt sur le revenu payé,lu_2011_account_10618,other,account_type_2011_1_capital,f, +lu_2011_account_106182,106182,Impôt sur la fortune payé,lu_2011_account_10618,other,account_type_2011_1_capital,f, +lu_2011_account_106183,106183,Impôt commercial - arriérés payés,lu_2011_account_10618,other,account_type_2011_1_capital,f, +lu_2011_account_106188,106188,Autres impôts,lu_2011_account_10618,other,account_type_2011_1_capital,f, +lu_2011_account_10619,10619,Prélèvements privés particuliers,lu_2011_account_1061,view,account_type_2011_1_capital,f, +lu_2011_account_106191,106191,Réparations aux immeubles privés,lu_2011_account_10619,other,account_type_2011_1_capital,f, +lu_2011_account_106192,106192,Placements sur comptes financiers privés,lu_2011_account_10619,other,account_type_2011_1_capital,f, +lu_2011_account_106193,106193,Remboursements de dettes privées,lu_2011_account_10619,other,account_type_2011_1_capital,f, +lu_2011_account_106194,106194,Dons et dotations aux enfants,lu_2011_account_10619,other,account_type_2011_1_capital,f, +lu_2011_account_106195,106195,Droits de succession et droits de mutation par décès,lu_2011_account_10619,other,account_type_2011_1_capital,f, +lu_2011_account_106198,106198,Autres prélèvements privés particuliers,lu_2011_account_10619,other,account_type_2011_1_capital,f, +lu_2011_account_1062,1062,Suppléments d'apports privés de l'exploitant ou des coexploitants,lu_2011_account_106,view,account_type_2011_1_capital,f, +lu_2011_account_10621,10621,Héritage ou donation,lu_2011_account_1062,other,account_type_2011_1_capital,f, +lu_2011_account_10622,10622,Avoirs privés,lu_2011_account_1062,other,account_type_2011_1_capital,f, +lu_2011_account_10623,10623,Emprunts privés,lu_2011_account_1062,other,account_type_2011_1_capital,f, +lu_2011_account_10624,10624,Cessions,lu_2011_account_1062,view,account_type_2011_1_capital,f, +lu_2011_account_106241,106241,Mobilier privé,lu_2011_account_10624,other,account_type_2011_1_capital,f, +lu_2011_account_106242,106242,Voiture privée,lu_2011_account_10624,other,account_type_2011_1_capital,f, +lu_2011_account_106243,106243,Titres privés,lu_2011_account_10624,other,account_type_2011_1_capital,f, +lu_2011_account_106244,106244,Immeubles privés,lu_2011_account_10624,other,account_type_2011_1_capital,f, +lu_2011_account_106248,106248,Autres cessions,lu_2011_account_10624,other,account_type_2011_1_capital,f, +lu_2011_account_10625,10625,Loyers encaissés,lu_2011_account_1062,other,account_type_2011_1_capital,f, +lu_2011_account_10626,10626,Salaires ou rentes touchés,lu_2011_account_1062,other,account_type_2011_1_capital,f, +lu_2011_account_10627,10627,Allocations familiales reçues,lu_2011_account_1062,other,account_type_2011_1_capital,f, +lu_2011_account_10628,10628,Remboursements d'impôts,lu_2011_account_1062,view,account_type_2011_1_capital,f, +lu_2011_account_106281,106281,Impôt sur le revenu,lu_2011_account_10628,other,account_type_2011_1_capital,f, +lu_2011_account_106283,106283,Impôt sur la fortune,lu_2011_account_10628,other,account_type_2011_1_capital,f, +lu_2011_account_106284,106284,Impôt commercial,lu_2011_account_10628,other,account_type_2011_1_capital,f, +lu_2011_account_106288,106288,Autres remboursements d'impôts,lu_2011_account_10628,other,account_type_2011_1_capital,f, +lu_2011_account_10629,10629,Quote-part professionnelle de frais privés,lu_2011_account_1062,other,account_type_2011_1_capital,f, +lu_2011_account_11,11,Primes d'émission et primes assimilées,lu_2011_account_1,view,account_type_2011_1_capital,f,account_financial_report_69 +lu_2011_account_111,111,Primes d'émission,lu_2011_account_11,other,account_type_2011_1_capital,f, +lu_2011_account_112,112,Primes de fusion,lu_2011_account_11,other,account_type_2011_1_capital,f, +lu_2011_account_113,113,Primes d'apport,lu_2011_account_11,other,account_type_2011_1_capital,f, +lu_2011_account_114,114,Primes de conversion d'obligations en actions,lu_2011_account_11,other,account_type_2011_1_capital,f, +lu_2011_account_115,115,"Apport en capitaux propres non rémunéré par des titres (""Capital contribution"")",lu_2011_account_11,other,account_type_2011_1_capital,f, +lu_2011_account_12,12,Réserves de réévaluation,lu_2011_account_1,view,account_type_2011_1_capital,f,account_financial_report_70 +lu_2011_account_121,121,Réserves de réévaluation en application de la juste valeur,lu_2011_account_12,other,account_type_2011_1_capital,f, +lu_2011_account_122,122,Réserves de mise en équivalence (Participations valorisées suivant l'art. 58),lu_2011_account_12,other,account_type_2011_1_capital,f, +lu_2011_account_123,123,Plus-values sur écarts de conversion immunisées,lu_2011_account_12,other,account_type_2011_1_capital,f, +lu_2011_account_128,128,Autres réserves de réévaluation,lu_2011_account_12,other,account_type_2011_1_capital,f, +lu_2011_account_13,13,Réserves,lu_2011_account_1,view,account_type_2011_1_capital,f, +lu_2011_account_131,131,Réserve légale,lu_2011_account_13,other,account_type_2011_1_capital,f,account_financial_report_72 +lu_2011_account_132,132,Réserve pour actions propres ou parts propres,lu_2011_account_13,other,account_type_2011_1_capital,f,account_financial_report_73 +lu_2011_account_133,133,Réserves statutaires,lu_2011_account_13,other,account_type_2011_1_capital,f,account_financial_report_74 +lu_2011_account_138,138,Autres réserves,lu_2011_account_13,view,account_type_2011_1_capital,f,account_financial_report_75 +lu_2011_account_1381,1381,Réserve pour l'impôt sur la fortune,lu_2011_account_138,other,account_type_2011_1_capital,f, +lu_2011_account_1382,1382,Autres réserves indisponibles,lu_2011_account_138,other,account_type_2011_1_capital,f, +lu_2011_account_1383,1383,Autres réserves disponibles,lu_2011_account_138,other,account_type_2011_1_capital,f, +lu_2011_account_14,14,Résultats,lu_2011_account_1,view,account_type_2011_1_capital,f, +lu_2011_account_141,141,Résultats reportés,lu_2011_account_14,other,account_type_2011_1_capital,f,account_financial_report_76 +lu_2011_account_142,142,Résultat de l'exercice,lu_2011_account_14,other,account_type_2011_1_capital,f,account_financial_report_77 +lu_2011_account_15,15,Acomptes sur dividendes,lu_2011_account_1,other,account_type_2011_1_capital,f,account_financial_report_78 +lu_2011_account_16,16,Subventions d'investissement en capital,lu_2011_account_1,view,account_type_2011_1_capital,f,account_financial_report_79 +lu_2011_account_161,161,Terrains et constructions,lu_2011_account_16,other,account_type_2011_1_capital,f, +lu_2011_account_162,162,Installations techniques et machines,lu_2011_account_16,other,account_type_2011_1_capital,f, +lu_2011_account_163,163,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_16,other,account_type_2011_1_capital,f, +lu_2011_account_168,168,Autres subventions d'investissement en capital,lu_2011_account_16,other,account_type_2011_1_capital,f, +lu_2011_account_17,17,Plus-values immunisées,lu_2011_account_1,view,account_type_2011_1_capital,f,account_financial_report_80 +lu_2011_account_171,171,Plus-values immunisées à réinvestir,lu_2011_account_17,other,account_type_2011_1_capital,f, +lu_2011_account_172,172,Plus-values immunisées réinvesties,lu_2011_account_17,other,account_type_2011_1_capital,f, +lu_2011_account_18,18,Provisions,lu_2011_account_1,view,account_type_2011_1_provision,f, +lu_2011_account_181,181,Provisions pour pensions et obligations similaires,lu_2011_account_18,other,account_type_2011_1_provision,f,account_financial_report_83 +lu_2011_account_182,182,Provisions pour impôts,lu_2011_account_18,view,account_type_2011_1_provision,f,account_financial_report_84 +lu_2011_account_1821,1821,Provisions pour impôt sur le revenu des collectivités,lu_2011_account_182,other,account_type_2011_1_provision,f, +lu_2011_account_1822,1822,Provisions pour impôt commercial,lu_2011_account_182,other,account_type_2011_1_provision,f, +lu_2011_account_1823,1823,Provisions pour impôt sur la fortune,lu_2011_account_182,other,account_type_2011_1_provision,f, +lu_2011_account_1828,1828,Autres provisions pour impôts,lu_2011_account_182,other,account_type_2011_1_provision,f, +lu_2011_account_183,183,Provisions pour impôts différés,lu_2011_account_18,other,account_type_2011_1_provision,f,account_financial_report_84 +lu_2011_account_188,188,Autres provisions,lu_2011_account_18,view,account_type_2011_1_provision,f,account_financial_report_85 +lu_2011_account_1881,1881,Provisions d'exploitation,lu_2011_account_188,other,account_type_2011_1_provision,f, +lu_2011_account_1882,1882,Provisions financières,lu_2011_account_188,other,account_type_2011_1_provision,f, +lu_2011_account_1883,1883,Provisions exceptionnelles,lu_2011_account_188,other,account_type_2011_1_provision,f, +lu_2011_account_19,19,Dettes financières et dettes assimilées,lu_2011_account_1,view,account_type_2011_1_provision,f, +lu_2011_account_191,191,Dettes subordonnées,lu_2011_account_19,view,account_type_2011_1_provision,f,account_financial_report_81 +lu_2011_account_1911,1911,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_191,view,account_type_2011_1_provision,f, +lu_2011_account_19111,19111,Montant principal,lu_2011_account_1911,other,account_type_2011_1_provision,f, +lu_2011_account_19112,19112,Intérêts courus,lu_2011_account_1911,other,account_type_2011_1_provision,f, +lu_2011_account_1912,1912,dont la durée résiduelle est supérieure à un an,lu_2011_account_191,view,account_type_2011_1_provision,f, +lu_2011_account_19121,19121,Montant principal,lu_2011_account_1912,other,account_type_2011_1_provision,f, +lu_2011_account_19122,19122,Intérêts courus,lu_2011_account_1912,other,account_type_2011_1_provision,f, +lu_2011_account_192,192,Emprunts obligataires convertibles,lu_2011_account_19,view,account_type_2011_1_provision,f, +lu_2011_account_1921,1921,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_192,view,account_type_2011_1_provision,f,account_financial_report_89 +lu_2011_account_19211,19211,Montant principal,lu_2011_account_1921,other,account_type_2011_1_provision,f, +lu_2011_account_19212,19212,Intérêts courus,lu_2011_account_1921,other,account_type_2011_1_provision,f, +lu_2011_account_1922,1922,dont la durée résiduelle est supérieure à un an,lu_2011_account_192,view,account_type_2011_1_provision,f,account_financial_report_90 +lu_2011_account_19221,19221,Montant principal,lu_2011_account_1922,other,account_type_2011_1_provision,f, +lu_2011_account_19222,19222,Intérêts courus,lu_2011_account_1922,other,account_type_2011_1_provision,f, +lu_2011_account_193,193,Emprunts obligataires non convertibles,lu_2011_account_19,view,account_type_2011_1_provision,f, +lu_2011_account_1931,1931,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_193,view,account_type_2011_1_provision,f,account_financial_report_92 +lu_2011_account_19311,19311,Montant principal,lu_2011_account_1931,other,account_type_2011_1_provision,f, +lu_2011_account_19312,19312,Intérêts courus,lu_2011_account_1931,other,account_type_2011_1_provision,f, +lu_2011_account_1932,1932,dont la durée résiduelle est supérieure à un an,lu_2011_account_193,view,account_type_2011_1_provision,f,account_financial_report_93 +lu_2011_account_19321,19321,Montant principal,lu_2011_account_1932,other,account_type_2011_1_provision,f, +lu_2011_account_19322,19322,Intérêts courus,lu_2011_account_1932,other,account_type_2011_1_provision,f, +lu_2011_account_194,194,Dettes envers des établissements de crédit,lu_2011_account_19,view,account_type_2011_1_provision,f, +lu_2011_account_1941,1941,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_194,view,account_type_2011_1_provision,f,account_financial_report_95 +lu_2011_account_19411,19411,Montant principal,lu_2011_account_1941,other,account_type_2011_1_provision,f, +lu_2011_account_19412,19412,Intérêts courus,lu_2011_account_1941,other,account_type_2011_1_provision,f, +lu_2011_account_1942,1942,dont la durée résiduelle est supérieure à un an,lu_2011_account_194,view,account_type_2011_1_provision,f,account_financial_report_96 +lu_2011_account_19421,19421,Montant principal,lu_2011_account_1942,other,account_type_2011_1_provision,f, +lu_2011_account_19422,19422,Intérêts courus,lu_2011_account_1942,other,account_type_2011_1_provision,f, +lu_2011_account_195,195,Dettes de leasing financier,lu_2011_account_19,view,account_type_2011_1_provision,f, +lu_2011_account_1951,1951,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_195,other,account_type_2011_1_provision,f,account_financial_report_95 +lu_2011_account_1952,1952,dont la durée résiduelle est supérieure à un an,lu_2011_account_195,other,account_type_2011_1_provision,f,account_financial_report_96 +lu_2011_account_198,198,Autres emprunts et dettes assimilées,lu_2011_account_19,view,account_type_2011_1_provision,f, +lu_2011_account_1981,1981,dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_198,view,account_type_2011_1_provision,f,account_financial_report_116 +lu_2011_account_19811,19811,Autres emprunts,lu_2011_account_1981,other,account_type_2011_1_provision,f, +lu_2011_account_19812,19812,Rentes viagères capitalisées,lu_2011_account_1981,other,account_type_2011_1_provision,f, +lu_2011_account_19813,19813,Autres dettes assimilées,lu_2011_account_1981,other,account_type_2011_1_provision,f, +lu_2011_account_19814,19814,Intérêts courus sur autres emprunts et dettes assimilées,lu_2011_account_1981,other,account_type_2011_1_provision,f, +lu_2011_account_1982,1982,dont la durée résiduelle est supérieure à un an,lu_2011_account_198,view,account_type_2011_1_provision,f,account_financial_report_117 +lu_2011_account_19821,19821,Autres emprunts,lu_2011_account_1982,other,account_type_2011_1_provision,f, +lu_2011_account_19822,19822,Rentes viagères capitalisées,lu_2011_account_1982,other,account_type_2011_1_provision,f, +lu_2011_account_19823,19823,Autres dettes assimilées,lu_2011_account_1982,other,account_type_2011_1_provision,f, +lu_2011_account_19824,19824,Intérêts courus sur autres emprunts et dettes assimilées,lu_2011_account_1982,other,account_type_2011_1_provision,f, +lu_2011_account_2,2,CLASSE 2 - COMPTES DE FRAIS D’ETABLISSEMENT ET D’ACTIFS IMMOBILISES,lu_2011_account_bilan,view,account.data_account_type_view,f, +lu_2011_account_20,20,Frais d'établissement et frais assimilés,lu_2011_account_2,view,account_type_2011_2_immo,f,account_financial_report_19 +lu_2011_account_201,201,Frais de constitution,lu_2011_account_20,other,account_type_2011_2_immo,f, +lu_2011_account_202,202,Frais de premier établissement,lu_2011_account_20,view,account_type_2011_2_immo,f, +lu_2011_account_2021,2021,Frais de prospection,lu_2011_account_202,other,account_type_2011_2_immo,f, +lu_2011_account_2022,2022,Frais de publicité,lu_2011_account_202,other,account_type_2011_2_immo,f, +lu_2011_account_203,203,"Frais d'augmentation de capital et d'opérations diverses (fusions, scissions, transformations)",lu_2011_account_20,other,account_type_2011_2_immo,f, +lu_2011_account_204,204,Frais d'émission d'emprunts,lu_2011_account_20,other,account_type_2011_2_immo,f, +lu_2011_account_208,208,Autres frais assimilés,lu_2011_account_20,other,account_type_2011_2_immo,f, +lu_2011_account_21,21,Immobilisations incorporelles,lu_2011_account_2,view,account_type_2011_2_immo,f, +lu_2011_account_211,211,Frais de recherche et de développement,lu_2011_account_21,other,account_type_2011_2_immo,f,account_financial_report_22 +lu_2011_account_212,212,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_21,view,account_type_2011_2_immo,f, +lu_2011_account_2121,2121,Acquis à titre onéreux (Actifs incorporels non produits),lu_2011_account_212,view,account_type_2011_2_immo,f,account_financial_report_24 +lu_2011_account_21211,21211,Concessions,lu_2011_account_2121,other,account_type_2011_2_immo,f, +lu_2011_account_21212,21212,Brevets,lu_2011_account_2121,other,account_type_2011_2_immo,f, +lu_2011_account_21213,21213,Licences informatiques (logiciels et progiciels informatiques),lu_2011_account_2121,other,account_type_2011_2_immo,f, +lu_2011_account_21214,21214,Marques et franchises,lu_2011_account_2121,other,account_type_2011_2_immo,f, +lu_2011_account_21215,21215,Droits et valeurs similaires acquis à titre onéreux,lu_2011_account_2121,view,account_type_2011_2_immo,f, +lu_2011_account_212151,212151,Droits d'auteur et de reproduction,lu_2011_account_21215,other,account_type_2011_2_immo,f, +lu_2011_account_212152,212152,Droits d'émission,lu_2011_account_21215,other,account_type_2011_2_immo,f, +lu_2011_account_212158,212158,Autres droits et valeurs similaires acquis à titre onéreux,lu_2011_account_21215,other,account_type_2011_2_immo,f, +lu_2011_account_2122,2122,Créés par l'entreprise elle-même (Actifs incorporels produits),lu_2011_account_212,view,account_type_2011_2_immo,f,account_financial_report_25 +lu_2011_account_21221,21221,Concessions,lu_2011_account_2122,other,account_type_2011_2_immo,f, +lu_2011_account_21222,21222,Brevets,lu_2011_account_2122,other,account_type_2011_2_immo,f, +lu_2011_account_21223,21223,Licences informatiques (logiciels et progiciels informatiques),lu_2011_account_2122,other,account_type_2011_2_immo,f, +lu_2011_account_21224,21224,Marques et franchises,lu_2011_account_2122,other,account_type_2011_2_immo,f, +lu_2011_account_21225,21225,Droits et valeurs similaires créés par l'entreprise elle-même,lu_2011_account_2122,view,account_type_2011_2_immo,f, +lu_2011_account_212251,212251,Droits d'auteur et de reproduction,lu_2011_account_21225,other,account_type_2011_2_immo,f, +lu_2011_account_212252,212252,Droits d'émission,lu_2011_account_21225,other,account_type_2011_2_immo,f, +lu_2011_account_212258,212258,Autres droits et valeurs similaires créés par l'entreprise elle-même,lu_2011_account_21225,other,account_type_2011_2_immo,f, +lu_2011_account_213,213,"Fonds de commerce, dans la mesure où il a été acquis à titre onéreux",lu_2011_account_21,other,account_type_2011_2_immo,f,account_financial_report_26 +lu_2011_account_214,214,Acomptes versés et immobilisations incorporelles en cours,lu_2011_account_21,view,account_type_2011_2_immo,f,account_financial_report_27 +lu_2011_account_2141,2141,Frais de recherche et de développement,lu_2011_account_214,other,account_type_2011_2_immo,f, +lu_2011_account_2142,2142,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_214,other,account_type_2011_2_immo,f, +lu_2011_account_2143,2143,Fonds de commerce,lu_2011_account_214,other,account_type_2011_2_immo,f, +lu_2011_account_22,22,Immobilisations corporelles,lu_2011_account_2,view,account_type_2011_2_immo,f, +lu_2011_account_221,221,Terrains et constructions,lu_2011_account_22,view,account_type_2011_2_immo,f,account_financial_report_29 +lu_2011_account_2211,2211,Terrains,lu_2011_account_221,view,account_type_2011_2_immo,f, +lu_2011_account_22111,22111,Terrains nus,lu_2011_account_2211,other,account_type_2011_2_immo,f, +lu_2011_account_22112,22112,Terrains aménagés,lu_2011_account_2211,other,account_type_2011_2_immo,f, +lu_2011_account_22113,22113,Sous-sols et sursols,lu_2011_account_2211,other,account_type_2011_2_immo,f, +lu_2011_account_22114,22114,Terrains de gisement,lu_2011_account_2211,other,account_type_2011_2_immo,f, +lu_2011_account_22115,22115,Terrains bâtis,lu_2011_account_2211,other,account_type_2011_2_immo,f, +lu_2011_account_22118,22118,Autres terrains,lu_2011_account_2211,other,account_type_2011_2_immo,f, +lu_2011_account_2212,2212,Agencements et aménagements de terrains,lu_2011_account_221,view,account_type_2011_2_immo,f, +lu_2011_account_22121,22121,Agencements et aménagements de terrains nus,lu_2011_account_2212,other,account_type_2011_2_immo,f, +lu_2011_account_22122,22122,Agencements et aménagements de terrains aménagés,lu_2011_account_2212,other,account_type_2011_2_immo,f, +lu_2011_account_22123,22123,Agencements et aménagements de sous-sols et sursols,lu_2011_account_2212,other,account_type_2011_2_immo,f, +lu_2011_account_22124,22124,Agencements et aménagements de terrains de gisement,lu_2011_account_2212,other,account_type_2011_2_immo,f, +lu_2011_account_22125,22125,Agencements et aménagements de terrains bâtis,lu_2011_account_2212,other,account_type_2011_2_immo,f, +lu_2011_account_22128,22128,Agencements et aménagements d'autres terrains,lu_2011_account_2212,other,account_type_2011_2_immo,f, +lu_2011_account_2213,2213,Constructions,lu_2011_account_221,view,account_type_2011_2_immo,f, +lu_2011_account_22131,22131,Constructions sur sol propre,lu_2011_account_2213,other,account_type_2011_2_immo,f, +lu_2011_account_22132,22132,Constructions sur sol d'autrui,lu_2011_account_2213,other,account_type_2011_2_immo,f, +lu_2011_account_222,222,Installations techniques et machines,lu_2011_account_22,view,account_type_2011_2_immo,f,account_financial_report_30 +lu_2011_account_2221,2221,Installations techniques,lu_2011_account_222,other,account_type_2011_2_immo,f, +lu_2011_account_2222,2222,Machines,lu_2011_account_222,other,account_type_2011_2_immo,f, +lu_2011_account_223,223,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_22,view,account_type_2011_2_immo,f,account_financial_report_31 +lu_2011_account_2231,2231,Equipement de transport et de manutention,lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_2232,2232,Véhicules de transport,lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_2233,2233,Outillage,lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_2234,2234,Mobilier,lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_2235,2235,Matériel informatique (hardware),lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_2236,2236,Cheptel,lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_2237,2237,Emballages récupérables,lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_2238,2238,Autres installations,lu_2011_account_223,other,account_type_2011_2_immo,f, +lu_2011_account_224,224,Acomptes versés et immobilisations corporelles en cours,lu_2011_account_22,view,account_type_2011_2_immo,f,account_financial_report_32 +lu_2011_account_2241,2241,Terrains et constructions,lu_2011_account_224,view,account_type_2011_2_immo,f, +lu_2011_account_22411,22411,Terrains,lu_2011_account_2241,other,account_type_2011_2_immo,f, +lu_2011_account_22412,22412,Agencements et aménagements de terrains,lu_2011_account_2241,other,account_type_2011_2_immo,f, +lu_2011_account_22413,22413,Constructions,lu_2011_account_2241,other,account_type_2011_2_immo,f, +lu_2011_account_2242,2242,Installations techniques et machines,lu_2011_account_224,other,account_type_2011_2_immo,f, +lu_2011_account_2243,2243,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_224,other,account_type_2011_2_immo,f, +lu_2011_account_23,23,Immobilisations financières,lu_2011_account_2,view,account_type_2011_2_immo,f, +lu_2011_account_231,231,Parts dans des entreprises liées,lu_2011_account_23,other,account_type_2011_2_immo,f,account_financial_report_34 +lu_2011_account_232,232,Créances sur des entreprises liées,lu_2011_account_23,other,account_type_2011_2_immo,f,account_financial_report_35 +lu_2011_account_233,233,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_23,other,account_type_2011_2_immo,f,account_financial_report_36 +lu_2011_account_234,234,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_23,other,account_type_2011_2_immo,f,account_financial_report_37 +lu_2011_account_235,235,Titres ayant le caractère d'immobilisations,lu_2011_account_23,view,account_type_2011_2_immo,f,account_financial_report_38 +lu_2011_account_2351,2351,Titres immobilisés (droit de propriété),lu_2011_account_235,view,account_type_2011_2_immo,f, +lu_2011_account_23511,23511,Actions,lu_2011_account_2351,other,account_type_2011_2_immo,f, +lu_2011_account_23518,23518,Autres titres immobilisés (droit de propriété),lu_2011_account_2351,other,account_type_2011_2_immo,f, +lu_2011_account_2352,2352,Titres immobilisés (droit de créance),lu_2011_account_235,view,account_type_2011_2_immo,f, +lu_2011_account_23521,23521,Obligations,lu_2011_account_2352,other,account_type_2011_2_immo,f, +lu_2011_account_23528,23528,Autres titres immobilisés (droit de créance),lu_2011_account_2352,other,account_type_2011_2_immo,f, +lu_2011_account_2358,2358,Autres titres ayant le caractère d'immobilisations,lu_2011_account_235,other,account_type_2011_2_immo,f, +lu_2011_account_236,236,Prêts et créances immobilisées,lu_2011_account_23,view,account_type_2011_2_immo,f,account_financial_report_39 +lu_2011_account_2361,2361,Prêts,lu_2011_account_236,view,account_type_2011_2_immo,f, +lu_2011_account_23611,23611,Prêts participatifs,lu_2011_account_2361,other,account_type_2011_2_immo,f, +lu_2011_account_23612,23612,Prêts aux associés,lu_2011_account_2361,other,account_type_2011_2_immo,f, +lu_2011_account_23613,23613,Prêts au personnel,lu_2011_account_2361,other,account_type_2011_2_immo,f, +lu_2011_account_23618,23618,Autres prêts,lu_2011_account_2361,other,account_type_2011_2_immo,f, +lu_2011_account_2362,2362,Dépôts et cautionnements versés,lu_2011_account_236,view,account_type_2011_2_immo,f, +lu_2011_account_23621,23621,Dépôts,lu_2011_account_2362,other,account_type_2011_2_immo,f, +lu_2011_account_23622,23622,Cautionnements,lu_2011_account_2362,other,account_type_2011_2_immo,f, +lu_2011_account_2363,2363,Créances immobilisées,lu_2011_account_236,other,account_type_2011_2_immo,f, +lu_2011_account_237,237,Actions propres ou parts propres,lu_2011_account_23,other,account_type_2011_2_immo,f,account_financial_report_40 +lu_2011_account_3,3,CLASSE 3 - COMPTES DE STOCKS,lu_2011_account_bilan,view,account.data_account_type_view,f, +lu_2011_account_30,30,Matières premières et consommables,lu_2011_account_3,view,account_type_2011_3_stock,f,account_financial_report_43 +lu_2011_account_301,301,Matières premières,lu_2011_account_30,other,account_type_2011_3_stock,f, +lu_2011_account_302,302,Matières consommables,lu_2011_account_30,other,account_type_2011_3_stock,f, +lu_2011_account_303,303,Fournitures consommables,lu_2011_account_30,view,account_type_2011_3_stock,f, +lu_2011_account_3031,3031,Combustibles,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_3032,3032,Produits d'entretien,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_3033,3033,Fournitures d'atelier et d'usine,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_3034,3034,Fournitures de magasin,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_3035,3035,Fournitures de bureau,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_3036,3036,Carburants,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_3037,3037,Lubrifiants,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_3038,3038,Autres fournitures consommables,lu_2011_account_303,other,account_type_2011_3_stock,f, +lu_2011_account_304,304,Emballages,lu_2011_account_30,view,account_type_2011_3_stock,f, +lu_2011_account_3041,3041,Emballages non-récupérables,lu_2011_account_304,other,account_type_2011_3_stock,f, +lu_2011_account_3042,3042,Emballages récupérables,lu_2011_account_304,other,account_type_2011_3_stock,f, +lu_2011_account_3043,3043,Emballages à usage mixte,lu_2011_account_304,other,account_type_2011_3_stock,f, +lu_2011_account_305,305,Approvisionnements,lu_2011_account_30,other,account_type_2011_3_stock,f, +lu_2011_account_31,31,Produits en cours de fabrication et commandes en cours,lu_2011_account_3,view,account_type_2011_3_stock,f,account_financial_report_44 +lu_2011_account_311,311,Produits en cours de fabrication,lu_2011_account_31,other,account_type_2011_3_stock,f, +lu_2011_account_312,312,Commandes en cours – Produits,lu_2011_account_31,other,account_type_2011_3_stock,f, +lu_2011_account_313,313,Commandes en cours – Prestations de services,lu_2011_account_31,other,account_type_2011_3_stock,f, +lu_2011_account_314,314,Immeubles en construction,lu_2011_account_31,other,account_type_2011_3_stock,f, +lu_2011_account_32,32,Produits finis et marchandises,lu_2011_account_3,view,account_type_2011_3_stock,f,account_financial_report_45 +lu_2011_account_321,321,Produits finis,lu_2011_account_32,other,account_type_2011_3_stock,f, +lu_2011_account_322,322,Produits intermédiaires,lu_2011_account_32,other,account_type_2011_3_stock,f, +lu_2011_account_323,323,Produits résiduels,lu_2011_account_32,view,account_type_2011_3_stock,f, +lu_2011_account_3231,3231,Déchets,lu_2011_account_323,other,account_type_2011_3_stock,f, +lu_2011_account_3232,3232,Rebuts,lu_2011_account_323,other,account_type_2011_3_stock,f, +lu_2011_account_3233,3233,Matières de récupération,lu_2011_account_323,other,account_type_2011_3_stock,f, +lu_2011_account_326,326,Marchandises,lu_2011_account_32,other,account_type_2011_3_stock,f, +lu_2011_account_327,327,"Marchandises en voie d'acheminement, mises en dépôt ou données en consignation",lu_2011_account_32,other,account_type_2011_3_stock,f, +lu_2011_account_33,33,Terrains et immeubles destinés à la revente,lu_2011_account_3,view,account_type_2011_3_stock,f,account_financial_report_45 +lu_2011_account_331,331,Terrains,lu_2011_account_33,other,account_type_2011_3_stock,f, +lu_2011_account_332,332,Immeubles,lu_2011_account_33,view,account_type_2011_3_stock,f, +lu_2011_account_3321,3321,Immeubles acquis,lu_2011_account_332,other,account_type_2011_3_stock,f, +lu_2011_account_3322,3322,Immeubles construits,lu_2011_account_332,other,account_type_2011_3_stock,f, +lu_2011_account_34,34,Acomptes versés,lu_2011_account_3,view,account_type_2011_3_stock,f,account_financial_report_46 +lu_2011_account_341,341,Acomptes versés sur matières premières et consommables,lu_2011_account_34,other,account_type_2011_3_stock,f, +lu_2011_account_342,342,Acomptes versés sur produits en cours de fabrication et commandes en cours,lu_2011_account_34,other,account_type_2011_3_stock,f, +lu_2011_account_343,343,Acomptes versés sur produits finis et marchandises,lu_2011_account_34,other,account_type_2011_3_stock,f, +lu_2011_account_344,344,Acomptes versés sur terrains et immeubles destinés à la revente,lu_2011_account_34,other,account_type_2011_3_stock,f, +lu_2011_account_4,4,CLASSE 4 - COMPTES DE TIERS,lu_2011_account_bilan,view,account.data_account_type_view,f, +lu_2011_account_40,40,Créances résultant de ventes et prestations de services,lu_2011_account_4,view,account_type_2011_4_creance,f, +lu_2011_account_401,401,Créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_40,view,account_type_2011_4_creance,f,account_financial_report_49 +lu_2011_account_4011,4011,Clients,lu_2011_account_401,receivable,account_type_2011_4_creance,t, +lu_2011_account_4012,4012,Clients – Effets à recevoir,lu_2011_account_401,receivable,account_type_2011_4_creance,t, +lu_2011_account_4013,4013,Clients douteux ou litigieux,lu_2011_account_401,receivable,account_type_2011_4_creance,t, +lu_2011_account_4014,4014,Clients – Factures à établir,lu_2011_account_401,receivable,account_type_2011_4_creance,t, +lu_2011_account_4015,4015,Clients créditeurs,lu_2011_account_401,receivable,account_type_2011_4_creance,t, +lu_2011_account_4019,4019,Corrections de valeur,lu_2011_account_401,other,account_type_2011_4_creance,f, +lu_2011_account_402,402,Créances dont la durée résiduelle est supérieure à un an,lu_2011_account_40,view,account_type_2011_4_creance,f,account_financial_report_50 +lu_2011_account_4021,4021,Clients,lu_2011_account_402,receivable,account_type_2011_4_creance,t, +lu_2011_account_4022,4022,Clients – Effets à recevoir,lu_2011_account_402,receivable,account_type_2011_4_creance,t, +lu_2011_account_4023,4023,Clients douteux ou litigieux,lu_2011_account_402,receivable,account_type_2011_4_creance,t, +lu_2011_account_4024,4024,Clients – Factures à établir,lu_2011_account_402,receivable,account_type_2011_4_creance,t, +lu_2011_account_4025,4025,Clients créditeurs,lu_2011_account_402,receivable,account_type_2011_4_creance,t, +lu_2011_account_4029,4029,Corrections de valeur,lu_2011_account_402,other,account_type_2011_4_creance,f, +lu_2011_account_41,41,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_4,view,account_type_2011_4_creance,f, +lu_2011_account_411,411,Créances sur des entreprises liées,lu_2011_account_41,view,account_type_2011_4_creance,f, +lu_2011_account_4111,4111,Créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_411,view,account_type_2011_4_creance,f,account_financial_report_52 +lu_2011_account_41111,41111,Ventes de marchandises et de prestations de services,lu_2011_account_4111,other,account_type_2011_4_creance,f, +lu_2011_account_41112,41112,Prêts et avances,lu_2011_account_4111,other,account_type_2011_4_creance,f, +lu_2011_account_41113,41113,Intérêts courus,lu_2011_account_4111,other,account_type_2011_4_creance,f, +lu_2011_account_41114,41114,Dividendes à recevoir,lu_2011_account_4111,other,account_type_2011_4_creance,f, +lu_2011_account_41118,41118,Autres créances,lu_2011_account_4111,other,account_type_2011_4_creance,f, +lu_2011_account_41119,41119,Corrections de valeur,lu_2011_account_4111,other,account_type_2011_4_creance,f, +lu_2011_account_4112,4112,Créances dont la durée résiduelle est supérieure à un an,lu_2011_account_411,view,account_type_2011_4_creance,f,account_financial_report_53 +lu_2011_account_41121,41121,Ventes de marchandises et de prestations de services,lu_2011_account_4112,other,account_type_2011_4_creance,f, +lu_2011_account_41122,41122,Prêts et avances,lu_2011_account_4112,other,account_type_2011_4_creance,f, +lu_2011_account_41123,41123,Intérêts courus,lu_2011_account_4112,other,account_type_2011_4_creance,f, +lu_2011_account_41124,41124,Dividendes à recevoir,lu_2011_account_4112,other,account_type_2011_4_creance,f, +lu_2011_account_41128,41128,Autres créances,lu_2011_account_4112,other,account_type_2011_4_creance,f, +lu_2011_account_41129,41129,Corrections de valeur,lu_2011_account_4112,other,account_type_2011_4_creance,f, +lu_2011_account_412,412,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_41,view,account_type_2011_4_creance,f, +lu_2011_account_4121,4121,Créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_412,view,account_type_2011_4_creance,f,account_financial_report_55 +lu_2011_account_41211,41211,Ventes de marchandises et de prestations de service,lu_2011_account_4121,other,account_type_2011_4_creance,f, +lu_2011_account_41212,41212,Prêts et avances,lu_2011_account_4121,other,account_type_2011_4_creance,f, +lu_2011_account_41213,41213,Intérêts courus,lu_2011_account_4121,other,account_type_2011_4_creance,f, +lu_2011_account_41214,41214,Dividendes à recevoir,lu_2011_account_4121,other,account_type_2011_4_creance,f, +lu_2011_account_41218,41218,Autres créances,lu_2011_account_4121,other,account_type_2011_4_creance,f, +lu_2011_account_41219,41219,Corrections de valeur,lu_2011_account_4121,other,account_type_2011_4_creance,f, +lu_2011_account_4122,4122,Créances dont la durée résiduelle est supérieure à un an,lu_2011_account_412,view,account_type_2011_4_creance,f,account_financial_report_56 +lu_2011_account_41221,41221,Ventes de marchandises et de prestations de service,lu_2011_account_4122,other,account_type_2011_4_creance,f, +lu_2011_account_41222,41222,Prêts et avances,lu_2011_account_4122,other,account_type_2011_4_creance,f, +lu_2011_account_41223,41223,Intérêts courus,lu_2011_account_4122,other,account_type_2011_4_creance,f, +lu_2011_account_41224,41224,Dividendes à recevoir,lu_2011_account_4122,other,account_type_2011_4_creance,f, +lu_2011_account_41228,41228,Autres créances,lu_2011_account_4122,other,account_type_2011_4_creance,f, +lu_2011_account_41229,41229,Corrections de valeur,lu_2011_account_4122,other,account_type_2011_4_creance,f, +lu_2011_account_42,42,Autres créances,lu_2011_account_4,view,account_type_2011_4_creance,f, +lu_2011_account_421,421,Autres créances dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_42,view,account_type_2011_4_creance,f,account_financial_report_58 +lu_2011_account_4211,4211,Personnel – Avances et acomptes,lu_2011_account_421,view,account_type_2011_4_creance,f, +lu_2011_account_42111,42111,Avances et acomptes,lu_2011_account_4211,other,account_type_2011_4_creance,f, +lu_2011_account_42119,42119,Corrections de valeur,lu_2011_account_4211,other,account_type_2011_4_creance,f, +lu_2011_account_4212,4212,Créances sur associés ou actionnaires,lu_2011_account_421,view,account_type_2011_4_creance,f, +lu_2011_account_42121,42121,Montant principal,lu_2011_account_4212,other,account_type_2011_4_creance,f, +lu_2011_account_42122,42122,Intérêts courus,lu_2011_account_4212,other,account_type_2011_4_creance,f, +lu_2011_account_42129,42129,Corrections de valeur sur créances,lu_2011_account_4212,other,account_type_2011_4_creance,f, +lu_2011_account_4213,4213,Etat – Subventions à recevoir,lu_2011_account_421,view,account_type_2011_4_creance,f, +lu_2011_account_42131,42131,Subventions d'investissement,lu_2011_account_4213,other,account_type_2011_4_creance,f, +lu_2011_account_42132,42132,Subventions d'exploitation,lu_2011_account_4213,other,account_type_2011_4_creance,f, +lu_2011_account_42138,42138,Autres subventions,lu_2011_account_4213,other,account_type_2011_4_creance,f, +lu_2011_account_4214,4214,Administration des Contributions Directes (ACD),lu_2011_account_421,other,account_type_2011_4_creance,f, +lu_2011_account_4215,4215,Administration des Douanes et Accises (ADA),lu_2011_account_421,other,account_type_2011_4_creance,f, +lu_2011_account_4216,4216,Administration de l'Enregistrement et des Domaines (AED),lu_2011_account_421,view,account_type_2011_4_creance,f, +lu_2011_account_42161,42161,Taxe sur la valeur ajoutée – TVA,lu_2011_account_4216,view,account_type_2011_4_creance,f, +lu_2011_account_421611,421611,TVA en amont,lu_2011_account_42161,other,account_type_2011_4_creance,f, +lu_2011_account_421612,421612,TVA à recevoir,lu_2011_account_42161,other,account_type_2011_4_creance,f, +lu_2011_account_421613,421613,TVA acomptes versés,lu_2011_account_42161,other,account_type_2011_4_creance,f, +lu_2011_account_421618,421618,TVA – Autres créances,lu_2011_account_42161,other,account_type_2011_4_creance,f, +lu_2011_account_42162,42162,Impôts indirects,lu_2011_account_4216,view,account_type_2011_4_creance,f, +lu_2011_account_421621,421621,Droits d'enregistrement,lu_2011_account_42162,other,account_type_2011_4_creance,f, +lu_2011_account_421622,421622,Taxe d'abonnement,lu_2011_account_42162,other,account_type_2011_4_creance,f, +lu_2011_account_421623,421623,Droits d'hypothèques,lu_2011_account_42162,other,account_type_2011_4_creance,f, +lu_2011_account_421624,421624,Droits de timbre,lu_2011_account_42162,other,account_type_2011_4_creance,f, +lu_2011_account_421628,421628,Autres impôts indirects,lu_2011_account_42162,other,account_type_2011_4_creance,f, +lu_2011_account_42168,42168,AED – Autres créances,lu_2011_account_4216,other,account_type_2011_4_creance,f, +lu_2011_account_4217,4217,Créances sur la sécurité sociale et autres organismes sociaux,lu_2011_account_421,view,account_type_2011_4_creance,f, +lu_2011_account_42171,42171,Centre Commun de Sécurité Sociale (CCSS),lu_2011_account_4217,other,account_type_2011_4_creance,f, +lu_2011_account_42172,42172,Mutualité des employeurs,lu_2011_account_4217,other,account_type_2011_4_creance,f, +lu_2011_account_42178,42178,Autres organismes sociaux,lu_2011_account_4217,other,account_type_2011_4_creance,f, +lu_2011_account_4218,4218,Créances diverses,lu_2011_account_421,view,account_type_2011_4_creance,f, +lu_2011_account_42181,42181,Impôts étrangers,lu_2011_account_4218,view,account_type_2011_4_creance,f, +lu_2011_account_421811,421811,TVA étrangères,lu_2011_account_42181,other,account_type_2011_4_creance,f, +lu_2011_account_421818,421818,Autres impôts étrangers,lu_2011_account_42181,other,account_type_2011_4_creance,f, +lu_2011_account_42188,42188,Autres créances diverses,lu_2011_account_4218,other,account_type_2011_4_creance,f, +lu_2011_account_42189,42189,Corrections de valeur,lu_2011_account_4218,other,account_type_2011_4_creance,f, +lu_2011_account_422,422,Autres créances dont la durée résiduelle est supérieure à un an,lu_2011_account_42,view,account_type_2011_4_creance,f,account_financial_report_59 +lu_2011_account_4221,4221,Personnel – Avances et acomptes,lu_2011_account_422,view,account_type_2011_4_creance,f, +lu_2011_account_42211,42211,Avances et acomptes,lu_2011_account_4221,other,account_type_2011_4_creance,f, +lu_2011_account_42219,42219,Corrections de valeur,lu_2011_account_4221,other,account_type_2011_4_creance,f, +lu_2011_account_4222,4222,Associés ou actionnaires,lu_2011_account_422,view,account_type_2011_4_creance,f, +lu_2011_account_42221,42221,Montant principal,lu_2011_account_4222,other,account_type_2011_4_creance,f, +lu_2011_account_42222,42222,Intérêts courus,lu_2011_account_4222,other,account_type_2011_4_creance,f, +lu_2011_account_42229,42229,Corrections de valeur sur créances,lu_2011_account_4222,other,account_type_2011_4_creance,f, +lu_2011_account_4223,4223,Etat – Subventions à recevoir,lu_2011_account_422,view,account_type_2011_4_creance,f, +lu_2011_account_42231,42231,Subventions d'investissement,lu_2011_account_4223,other,account_type_2011_4_creance,f, +lu_2011_account_42232,42232,Subventions d'exploitation,lu_2011_account_4223,other,account_type_2011_4_creance,f, +lu_2011_account_42238,42238,Autres subventions,lu_2011_account_4223,other,account_type_2011_4_creance,f, +lu_2011_account_4224,4224,Administration des Contributions Directes (ACD),lu_2011_account_422,other,account_type_2011_4_creance,f, +lu_2011_account_4225,4225,Administration des Douanes et Accises (ADA),lu_2011_account_422,other,account_type_2011_4_creance,f, +lu_2011_account_4226,4226,Administration de l'Enregistrement et des Domaines (AED),lu_2011_account_422,view,account_type_2011_4_creance,f, +lu_2011_account_42261,42261,Taxe sur la valeur ajoutée – TVA,lu_2011_account_4226,view,account_type_2011_4_creance,f, +lu_2011_account_422611,422611,TVA en amont,lu_2011_account_42261,view,account_type_2011_4_creance,f, +lu_2011_account_4226111,4226111,TVA en amont – Pays,lu_2011_account_422611,other,account_type_2011_4_creance,f, +lu_2011_account_4226112,4226112,TVA en amont – Intracommunautaire,lu_2011_account_422611,other,account_type_2011_4_creance,f, +lu_2011_account_4226113,4226113,TVA en amont – Triangulaire,lu_2011_account_422611,other,account_type_2011_4_creance,f, +lu_2011_account_4226114,4226114,TVA en amont – Exonérations spéciales,lu_2011_account_422611,other,account_type_2011_4_creance,f, +lu_2011_account_422612,422612,TVA à recevoir,lu_2011_account_42261,other,account_type_2011_4_creance,f, +lu_2011_account_422613,422613,TVA acomptes versés,lu_2011_account_42261,other,account_type_2011_4_creance,f, +lu_2011_account_422618,422618,TVA – Autres créances,lu_2011_account_42261,other,account_type_2011_4_creance,f, +lu_2011_account_42262,42262,Impôts indirects,lu_2011_account_4226,view,account_type_2011_4_creance,f, +lu_2011_account_422621,422621,Droits d'enregistrement,lu_2011_account_42262,other,account_type_2011_4_creance,f, +lu_2011_account_422622,422622,Taxe d'abonnement,lu_2011_account_42262,other,account_type_2011_4_creance,f, +lu_2011_account_422623,422623,Droits d'hypothèques,lu_2011_account_42262,other,account_type_2011_4_creance,f, +lu_2011_account_422624,422624,Droits de timbre,lu_2011_account_42262,other,account_type_2011_4_creance,f, +lu_2011_account_422628,422628,Autres impôts indirects,lu_2011_account_42262,other,account_type_2011_4_creance,f, +lu_2011_account_4227,4227,Créances sur la sécurité sociale et autres organismes sociaux,lu_2011_account_422,view,account_type_2011_4_creance,f, +lu_2011_account_42271,42271,Centre Commun de Sécurité Sociale (CCSS),lu_2011_account_4227,other,account_type_2011_4_creance,f, +lu_2011_account_42272,42272,Mutualité des employeurs,lu_2011_account_4227,other,account_type_2011_4_creance,f, +lu_2011_account_42278,42278,Autres organismes sociaux,lu_2011_account_4227,other,account_type_2011_4_creance,f, +lu_2011_account_4228,4228,Créances diverses,lu_2011_account_422,view,account_type_2011_4_creance,f, +lu_2011_account_42281,42281,Impôts étrangers,lu_2011_account_4228,view,account_type_2011_4_creance,f, +lu_2011_account_422811,422811,TVA étrangères,lu_2011_account_42281,other,account_type_2011_4_creance,f, +lu_2011_account_422818,422818,Autres impôts étrangers,lu_2011_account_42281,other,account_type_2011_4_creance,f, +lu_2011_account_42288,42288,Autres créances diverses,lu_2011_account_4228,other,account_type_2011_4_creance,f, +lu_2011_account_42289,42289,Corrections de valeur sur autres créances diverses,lu_2011_account_4228,other,account_type_2011_4_creance,f, +lu_2011_account_43,43,Acomptes reçus sur commandes pour autant qu'ils ne sont pas déduits des stocks de façon distincte,lu_2011_account_4,view,account_type_2011_4_dette,f, +lu_2011_account_431,431,Acomptes reçus dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_43,other,account_type_2011_4_dette,f,account_financial_report_98 +lu_2011_account_432,432,Acomptes reçus dont la durée résiduelle est supérieure à un an,lu_2011_account_43,other,account_type_2011_4_dette,f,account_financial_report_99 +lu_2011_account_44,44,Dettes sur achats et prestations de services et dettes représentées par des effets de commerce,lu_2011_account_4,view,account_type_2011_4_dette,f, +lu_2011_account_441,441,Dettes sur achats et prestations de services,lu_2011_account_44,view,account_type_2011_4_dette,f, +lu_2011_account_4411,4411,Dettes sur achats et prestations de services dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_441,view,account_type_2011_4_dette,f,account_financial_report_101 +lu_2011_account_44111,44111,Fournisseurs,lu_2011_account_4411,payable,account_type_2011_4_dette,t, +lu_2011_account_44112,44112,Fournisseurs – Factures non parvenues,lu_2011_account_4411,payable,account_type_2011_4_dette,t, +lu_2011_account_44113,44113,Fournisseurs débiteurs,lu_2011_account_4411,view,account_type_2011_4_dette,f, +lu_2011_account_441131,441131,Fournisseurs – Avances et acomptes versés sur commandes,lu_2011_account_44113,payable,account_type_2011_4_dette,t, +lu_2011_account_441132,441132,Fournisseurs – Créances pour emballages et matériel à rendre,lu_2011_account_44113,payable,account_type_2011_4_dette,t, +lu_2011_account_441133,441133,Fournisseurs – Autres avoirs,lu_2011_account_44113,payable,account_type_2011_4_dette,t, +lu_2011_account_441134,441134,"Rabais, remises, ristournes à obtenir et autres avoirs non encore reçus",lu_2011_account_44113,payable,account_type_2011_4_dette,t, +lu_2011_account_4412,4412,Dettes sur achats et prestations de services dont la durée résiduelle est supérieure à un an,lu_2011_account_441,view,account_type_2011_4_dette,f,account_financial_report_102 +lu_2011_account_44121,44121,Fournisseurs,lu_2011_account_4412,payable,account_type_2011_4_dette,t, +lu_2011_account_44122,44122,Fournisseurs – Factures non parvenues,lu_2011_account_4412,payable,account_type_2011_4_dette,t, +lu_2011_account_44123,44123,Fournisseurs débiteurs,lu_2011_account_4412,view,account_type_2011_4_dette,f, +lu_2011_account_441231,441231,Fournisseurs – Avances et acomptes versés sur commandes,lu_2011_account_44123,payable,account_type_2011_4_dette,t, +lu_2011_account_441232,441232,Fournisseurs – Créances pour emballages et matériel à rendre,lu_2011_account_44123,payable,account_type_2011_4_dette,t, +lu_2011_account_441233,441233,Fournisseurs – Autres avoirs,lu_2011_account_44123,payable,account_type_2011_4_dette,t, +lu_2011_account_441234,441234,"Rabais, remises, ristournes à obtenir et autres avoirs non encore reçus",lu_2011_account_44123,payable,account_type_2011_4_dette,t, +lu_2011_account_442,442,Dettes représentées par des effets de commerce,lu_2011_account_44,view,account_type_2011_4_dette,f, +lu_2011_account_4421,4421,Dettes représentées par des effets de commerce dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_442,other,account_type_2011_4_dette,f,account_financial_report_104 +lu_2011_account_4422,4422,Dettes représentées par des effets de commerce dont la durée résiduelle est supérieure à un an,lu_2011_account_442,other,account_type_2011_4_dette,f,account_financial_report_105 +lu_2011_account_45,45,Dettes envers des entreprises liées et des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_4,view,account_type_2011_4_dette,f, +lu_2011_account_451,451,Dettes envers des entreprises liées,lu_2011_account_45,view,account_type_2011_4_dette,f, +lu_2011_account_4511,4511,Dettes envers des entreprises liées dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_451,view,account_type_2011_4_dette,f,account_financial_report_107 +lu_2011_account_45111,45111,Ventes de marchandises et de prestations de services,lu_2011_account_4511,other,account_type_2011_4_dette,f, +lu_2011_account_45112,45112,Prêts et avances,lu_2011_account_4511,other,account_type_2011_4_dette,f, +lu_2011_account_45113,45113,Intérêts courus,lu_2011_account_4511,other,account_type_2011_4_dette,f, +lu_2011_account_45114,45114,Dividendes à payer,lu_2011_account_4511,other,account_type_2011_4_dette,f, +lu_2011_account_45118,45118,Autres dettes,lu_2011_account_4511,other,account_type_2011_4_dette,f, +lu_2011_account_4512,4512,Dettes envers des entreprises liées dont la durée résiduelle est supérieure à un an,lu_2011_account_451,view,account_type_2011_4_dette,f,account_financial_report_108 +lu_2011_account_45121,45121,Ventes de marchandises et de prestations de services,lu_2011_account_4512,other,account_type_2011_4_dette,f, +lu_2011_account_45122,45122,Prêts et avances,lu_2011_account_4512,other,account_type_2011_4_dette,f, +lu_2011_account_45123,45123,Intérêts courus,lu_2011_account_4512,other,account_type_2011_4_dette,f, +lu_2011_account_45124,45124,Dividendes à payer,lu_2011_account_4512,other,account_type_2011_4_dette,f, +lu_2011_account_45128,45128,Autres dettes,lu_2011_account_4512,other,account_type_2011_4_dette,f, +lu_2011_account_452,452,Dettes envers des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_45,view,account_type_2011_4_dette,f, +lu_2011_account_4521,4521,Dettes envers des entreprises avec lesquelles la société a un lien de participation dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_452,view,account_type_2011_4_dette,f,account_financial_report_110 +lu_2011_account_45211,45211,Ventes de marchandises et de prestations de services,lu_2011_account_4521,other,account_type_2011_4_dette,f, +lu_2011_account_45212,45212,Prêts et avances,lu_2011_account_4521,other,account_type_2011_4_dette,f, +lu_2011_account_45213,45213,Intérêts courus,lu_2011_account_4521,other,account_type_2011_4_dette,f, +lu_2011_account_45214,45214,Dividendes à payer,lu_2011_account_4521,other,account_type_2011_4_dette,f, +lu_2011_account_45218,45218,Autres dettes,lu_2011_account_4521,other,account_type_2011_4_dette,f, +lu_2011_account_4522,4522,Dettes envers des entreprises avec lesquelles la société a un lien de participation dont la durée résiduelle est supérieure à un an,lu_2011_account_452,view,account_type_2011_4_dette,f,account_financial_report_111 +lu_2011_account_45221,45221,Ventes de marchandises et de prestations de services,lu_2011_account_4522,other,account_type_2011_4_dette,f, +lu_2011_account_45222,45222,Prêts et avances,lu_2011_account_4522,other,account_type_2011_4_dette,f, +lu_2011_account_45223,45223,Intérêts courus,lu_2011_account_4522,other,account_type_2011_4_dette,f, +lu_2011_account_45224,45224,Dividendes à payer,lu_2011_account_4522,other,account_type_2011_4_dette,f, +lu_2011_account_45228,45228,Autres dettes,lu_2011_account_4522,other,account_type_2011_4_dette,f, +lu_2011_account_46,46,Dettes fiscales et dettes envers la sécurité sociale,lu_2011_account_4,view,account_type_2011_4_dette,f, +lu_2011_account_461,461,Dettes fiscales,lu_2011_account_46,view,account_type_2011_4_dette,f,account_financial_report_113 +lu_2011_account_4611,4611,Administrations communales,lu_2011_account_461,view,account_type_2011_4_dette,f, +lu_2011_account_46111,46111,Impôts communaux,lu_2011_account_4611,other,account_type_2011_4_dette,f, +lu_2011_account_46112,46112,Taxes communales,lu_2011_account_4611,other,account_type_2011_4_dette,f, +lu_2011_account_4612,4612,Administration des Contributions Directes (ACD),lu_2011_account_461,view,account_type_2011_4_dette,f, +lu_2011_account_46121,46121,Impôt sur le revenu des collectivités,lu_2011_account_4612,view,account_type_2011_4_dette,f, +lu_2011_account_461211,461211,Impôt sur le revenu des collectivités – charge fiscale estimée,lu_2011_account_46121,other,account_type_2011_4_dette,f, +lu_2011_account_461212,461212,Impôt sur le revenu des collectivités – dette fiscale à payer,lu_2011_account_46121,other,account_type_2011_4_dette,f, +lu_2011_account_46122,46122,Impôt commercial,lu_2011_account_4612,view,account_type_2011_4_dette,f, +lu_2011_account_461221,461221,Impôt commercial – charge fiscale estimée,lu_2011_account_46122,other,account_type_2011_4_dette,f, +lu_2011_account_461222,461222,Impôt commercial – dette fiscale à payer,lu_2011_account_46122,other,account_type_2011_4_dette,f, +lu_2011_account_46123,46123,Impôt sur la fortune,lu_2011_account_4612,view,account_type_2011_4_dette,f, +lu_2011_account_461231,461231,Impôt sur la fortune – charge fiscale estimée,lu_2011_account_46123,other,account_type_2011_4_dette,f, +lu_2011_account_461232,461232,Impôt sur la fortune – dette fiscale à payer,lu_2011_account_46123,other,account_type_2011_4_dette,f, +lu_2011_account_46124,46124,Retenue d'impôt sur traitements et salaires,lu_2011_account_4612,other,account_type_2011_4_dette,f, +lu_2011_account_46125,46125,Retenue d'impôt sur revenus de capitaux mobiliers,lu_2011_account_4612,other,account_type_2011_4_dette,f, +lu_2011_account_46126,46126,Retenue d'impôt sur les tantièmes,lu_2011_account_4612,other,account_type_2011_4_dette,f, +lu_2011_account_46128,46128,ACD – Autres dettes,lu_2011_account_4612,other,account_type_2011_4_dette,f, +lu_2011_account_4613,4613,Administration des Douanes et Accises (ADA),lu_2011_account_461,view,account_type_2011_4_dette,f, +lu_2011_account_46131,46131,Taxe sur les véhicules automoteurs,lu_2011_account_4613,other,account_type_2011_4_dette,f, +lu_2011_account_46132,46132,Droits d'accises et taxe de consommation,lu_2011_account_4613,other,account_type_2011_4_dette,f, +lu_2011_account_46138,46138,ADA – Autres dettes,lu_2011_account_4613,other,account_type_2011_4_dette,f, +lu_2011_account_4614,4614,Administration de l'Enregistrement et des Domaines (AED),lu_2011_account_461,view,account_type_2011_4_dette,f, +lu_2011_account_46141,46141,Taxe sur la valeur ajoutée – TVA,lu_2011_account_4614,view,account_type_2011_4_dette,f, +lu_2011_account_461411,461411,TVA en aval,lu_2011_account_46141,view,account_type_2011_4_dette,f, +lu_2011_account_4614111,4614111,TVA en aval – Pays,lu_2011_account_461411,other,account_type_2011_4_dette,f, +lu_2011_account_4614112,4614112,TVA en aval – Intracommunautaire,lu_2011_account_461411,other,account_type_2011_4_dette,f, +lu_2011_account_4614113,4614113,TVA en aval – Extracommunautaire,lu_2011_account_461411,other,account_type_2011_4_dette,f, +lu_2011_account_4614114,4614114,TVA en aval – Triangulaire,lu_2011_account_461411,other,account_type_2011_4_dette,f, +lu_2011_account_4614115,4614115,TVA en aval – Exonérations spéciales,lu_2011_account_461411,other,account_type_2011_4_dette,f, +lu_2011_account_461412,461412,TVA due,lu_2011_account_46141,other,account_type_2011_4_dette,f, +lu_2011_account_461413,461413,TVA acomptes reçus,lu_2011_account_46141,other,account_type_2011_4_dette,f, +lu_2011_account_461418,461418,TVA – Autres dettes,lu_2011_account_46141,other,account_type_2011_4_dette,f, +lu_2011_account_46142,46142,Impôts indirects,lu_2011_account_4614,view,account_type_2011_4_dette,f, +lu_2011_account_461421,461421,Droits d'enregistrement,lu_2011_account_46142,other,account_type_2011_4_dette,f, +lu_2011_account_461422,461422,Taxe d'abonnement,lu_2011_account_46142,other,account_type_2011_4_dette,f, +lu_2011_account_461423,461423,Droits d'hypothèques,lu_2011_account_46142,other,account_type_2011_4_dette,f, +lu_2011_account_461424,461424,Droits de timbre,lu_2011_account_46142,other,account_type_2011_4_dette,f, +lu_2011_account_461428,461428,Autres impôts indirects,lu_2011_account_46142,other,account_type_2011_4_dette,f, +lu_2011_account_4615,4615,Administrations fiscales étrangères,lu_2011_account_461,other,account_type_2011_4_dette,f, +lu_2011_account_462,462,Dettes au titre de la sécurité sociale,lu_2011_account_46,view,account_type_2011_4_dette,f,account_financial_report_114 +lu_2011_account_4621,4621,Centre Commun de Sécurité Sociale (CCSS),lu_2011_account_462,other,account_type_2011_4_dette,f, +lu_2011_account_4622,4622,Organismes de sécurité sociale étrangers,lu_2011_account_462,other,account_type_2011_4_dette,f, +lu_2011_account_4628,4628,Autres organismes sociaux,lu_2011_account_462,other,account_type_2011_4_dette,f, +lu_2011_account_47,47,Autres dettes,lu_2011_account_4,view,account_type_2011_4_dette,f, +lu_2011_account_471,471,Autres dettes dont la durée résiduelle est inférieure ou égale à un an,lu_2011_account_47,view,account_type_2011_4_dette,f,account_financial_report_116 +lu_2011_account_4711,4711,Dépôts et cautionnements reçus,lu_2011_account_471,view,account_type_2011_4_dette,f, +lu_2011_account_47111,47111,Dépôts,lu_2011_account_4711,other,account_type_2011_4_dette,f, +lu_2011_account_47112,47112,Cautionnements,lu_2011_account_4711,other,account_type_2011_4_dette,f, +lu_2011_account_47113,47113,Intérêts courus,lu_2011_account_4711,other,account_type_2011_4_dette,f, +lu_2011_account_4712,4712,Dettes envers associés et actionnaires,lu_2011_account_471,view,account_type_2011_4_dette,f, +lu_2011_account_47121,47121,Montant principal,lu_2011_account_4712,other,account_type_2011_4_dette,f, +lu_2011_account_47122,47122,Intérêts courus,lu_2011_account_4712,other,account_type_2011_4_dette,f, +lu_2011_account_4713,4713,"Dettes envers administrateurs, gérants et commissaires",lu_2011_account_471,other,account_type_2011_4_dette,f, +lu_2011_account_4714,4714,Dettes envers le personnel,lu_2011_account_471,view,account_type_2011_4_dette,f, +lu_2011_account_47141,47141,Personnel – Rémunérations dues,lu_2011_account_4714,other,account_type_2011_4_dette,f, +lu_2011_account_47142,47142,Personnel – Dépôts,lu_2011_account_4714,other,account_type_2011_4_dette,f, +lu_2011_account_47143,47143,"Personnel – Oppositions, saisies",lu_2011_account_4714,other,account_type_2011_4_dette,f, +lu_2011_account_47148,47148,Personnel – Autres,lu_2011_account_4714,other,account_type_2011_4_dette,f, +lu_2011_account_4715,4715,Etat – Droits d'émission à restituer,lu_2011_account_471,other,account_type_2011_4_dette,f, +lu_2011_account_4718,4718,Autres dettes diverses,lu_2011_account_471,other,account_type_2011_4_dette,f, +lu_2011_account_472,472,Autres dettes dont la durée résiduelle est supérieure à un an,lu_2011_account_47,view,account_type_2011_4_dette,f,account_financial_report_117 +lu_2011_account_4721,4721,Dépôts et cautionnements reçus,lu_2011_account_472,view,account_type_2011_4_dette,f, +lu_2011_account_47211,47211,Dépôts,lu_2011_account_4721,other,account_type_2011_4_dette,f, +lu_2011_account_47212,47212,Cautionnements,lu_2011_account_4721,other,account_type_2011_4_dette,f, +lu_2011_account_47213,47213,Intérêts courus,lu_2011_account_4721,other,account_type_2011_4_dette,f, +lu_2011_account_4722,4722,Dettes envers associés et actionnaires,lu_2011_account_472,view,account_type_2011_4_dette,f, +lu_2011_account_47221,47221,Montant principal,lu_2011_account_4722,other,account_type_2011_4_dette,f, +lu_2011_account_47222,47222,Intérêts courus,lu_2011_account_4722,other,account_type_2011_4_dette,f, +lu_2011_account_4723,4723,"Dettes envers administrateurs, gérants et commissaires",lu_2011_account_472,other,account_type_2011_4_dette,f, +lu_2011_account_4724,4724,Dettes envers le personnel,lu_2011_account_472,view,account_type_2011_4_dette,f, +lu_2011_account_47241,47241,Personnel – Rémunérations dues,lu_2011_account_4724,other,account_type_2011_4_dette,f, +lu_2011_account_47242,47242,Personnel – Dépôts,lu_2011_account_4724,other,account_type_2011_4_dette,f, +lu_2011_account_47243,47243,"Personnel – Oppositions, saisies",lu_2011_account_4724,other,account_type_2011_4_dette,f, +lu_2011_account_47248,47248,Personnel – Autres,lu_2011_account_4724,other,account_type_2011_4_dette,f, +lu_2011_account_4726,4726,Etat – Droits d'émission à restituer,lu_2011_account_472,other,account_type_2011_4_dette,f, +lu_2011_account_4728,4728,Autres dettes diverses,lu_2011_account_472,other,account_type_2011_4_dette,f, +lu_2011_account_48,48,Comptes de régularisation,lu_2011_account_4,view,account.data_account_type_view,f, +lu_2011_account_481,481,Charges à reporter,lu_2011_account_48,other,account_type_2011_4_regul_actif,f,account_financial_report_65 +lu_2011_account_482,482,Produits à reporter,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,account_financial_report_118 +lu_2011_account_483,483,Etat - Droits d'émission alloués,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,account_financial_report_118 +lu_2011_account_484,484,Comptes transitoires ou d'attente – Actif,lu_2011_account_48,other,account_type_2011_4_regul_actif,f,account_financial_report_65 +lu_2011_account_485,485,Comptes transitoires ou d'attente – Passif,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,account_financial_report_118 +lu_2011_account_486,486,Comptes de liaison – Actif,lu_2011_account_48,other,account_type_2011_4_regul_actif,f,account_financial_report_65 +lu_2011_account_487,487,Comptes de liaison – Passif,lu_2011_account_48,other,account_type_2011_4_regul_passif,f,account_financial_report_118 +lu_2011_account_5,5,CLASSE 5 - COMPTES FINANCIERS,lu_2011_account_bilan,view,account.data_account_type_view,f, +lu_2011_account_50,50,Valeurs mobilières,lu_2011_account_5,view,account_type_2011_5_disponibilite,f, +lu_2011_account_501,501,Parts dans des entreprises liées,lu_2011_account_50,other,account_type_2011_5_disponibilite,f,account_financial_report_61 +lu_2011_account_502,502,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_50,other,account_type_2011_5_disponibilite,f,account_financial_report_61 +lu_2011_account_503,503,Actions propres ou parts propres,lu_2011_account_50,other,account_type_2011_5_disponibilite,f,account_financial_report_62 +lu_2011_account_508,508,Autres valeurs mobilières,lu_2011_account_50,view,account_type_2011_5_disponibilite,f,account_financial_report_63 +lu_2011_account_5081,5081,Actions – Titres cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, +lu_2011_account_5082,5082,Actions – Titres non cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, +lu_2011_account_5083,5083,Obligations et autres titres de créance émis par la société et rachetés par elle,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, +lu_2011_account_5084,5084,Obligations – Titres cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, +lu_2011_account_5085,5085,Obligations – Titres non cotés,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, +lu_2011_account_5088,5088,Autres valeurs mobilières diverses,lu_2011_account_508,other,account_type_2011_5_disponibilite,f, +lu_2011_account_51,51,"Avoirs en banques, avoirs en comptes de chèques postaux, chèques et encaisse",lu_2011_account_5,view,account_type_2011_5_disponibilite,f,account_financial_report_64 +lu_2011_account_511,511,Chèques à encaisser,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, +lu_2011_account_512,512,Valeurs à l'encaissement,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, +lu_2011_account_513,513,Banques,lu_2011_account_51,view,account_type_2011_5_disponibilite,f, +lu_2011_account_5131,5131,Banques comptes courants,lu_2011_account_513,view,account_type_2011_5_disponibilite,f, +lu_2011_account_5132,5132,Banques comptes à terme,lu_2011_account_513,view,account_type_2011_5_disponibilite,f, +lu_2011_account_514,514,Compte chèque postal,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, +lu_2011_account_516,516,Caisse,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, +lu_2011_account_517,517,Virements internes,lu_2011_account_51,other,account_type_2011_5_disponibilite,t, +lu_2011_account_518,518,Autres avoirs,lu_2011_account_51,other,account_type_2011_5_disponibilite,f, +lu_2011_account_resultat,resultat,TOTAL CLASSES 6 ET 7,lu_2011_account_0,view,account.data_account_type_view,,"account_financial_report_77,account_financial_report_161" +lu_2011_account_6,6,CLASSE 6 - COMPTES DE CHARGES,lu_2011_account_resultat,view,account.data_account_type_view,f, +lu_2011_account_60,60,Consommation de marchandises et de matières premières et consommables,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f,account_financial_report_120 +lu_2011_account_601,601,Matières premières,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_602,602,Matières consommables,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_603,603,Fournitures consommables,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6031,6031,Combustibles,lu_2011_account_603,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60311,60311,Solides,lu_2011_account_6031,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60312,60312,Liquides,lu_2011_account_6031,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60313,60313,Gaz comprimé,lu_2011_account_6031,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6032,6032,Produits d'entretien,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6033,6033,Fournitures d'atelier et d'usine,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6034,6034,Fournitures de magasin,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6035,6035,Fournitures de bureau,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6036,6036,Carburants,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6037,6037,Lubrifiants,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6038,6038,Autres fournitures consommables,lu_2011_account_603,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_604,604,Emballages,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6041,6041,Emballages non récupérables,lu_2011_account_604,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6042,6042,Emballages récupérables,lu_2011_account_604,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6043,6043,Emballages à usage mixte,lu_2011_account_604,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_605,605,Approvisionnements,lu_2011_account_60,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_606,606,Achats de biens destinés à la revente,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6061,6061,Terrains,lu_2011_account_606,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6062,6062,Immeubles,lu_2011_account_606,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6063,6063,Marchandises,lu_2011_account_606,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_607,607,Variation des stocks,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6071,6071,Variation des stocks de matières premières,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6072,6072,Variation des stocks de matières consommables,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6073,6073,Variation des stocks de fournitures consommables,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6074,6074,Variation des stocks d'emballages,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6075,6075,Variation des stocks d'approvisionnements,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6076,6076,Variation des stocks de biens destinés à la revente,lu_2011_account_607,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_608,608,Achats non stockés et achats incorporés aux ouvrages et produits,lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6081,6081,Achats non stockés de matières et fournitures,lu_2011_account_608,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60811,60811,Fournitures non stockables,lu_2011_account_6081,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_608111,608111,Eau,lu_2011_account_60811,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_608112,608112,Electricité,lu_2011_account_60811,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_608113,608113,Gaz de canalisation,lu_2011_account_60811,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60812,60812,Fournitures d'entretien et de petit équipement,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60813,60813,Fournitures administratives,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60814,60814,Carburants,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60815,60815,Lubrifiants,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60816,60816,Vêtements professionnels,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60818,60818,Autres matières et fournitures non stockées,lu_2011_account_6081,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6082,6082,Achats incorporés aux ouvrages et produits,lu_2011_account_608,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60821,60821,Achats d'études et prestations de service (incorporés aux ouvrages et produits),lu_2011_account_6082,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_608211,608211,Travail à façon,lu_2011_account_60821,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_608212,608212,Recherche et développement,lu_2011_account_60821,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_608213,608213,Frais d'architectes et d'ingénieurs,lu_2011_account_60821,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60822,60822,"Achats de matériel, équipements, pièces détachées et travaux (incorporés aux ouvrages et produits)",lu_2011_account_6082,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_60828,60828,Autres achats d'études et de prestations de service,lu_2011_account_6082,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_609,609,"Rabais, remises et ristournes obtenus",lu_2011_account_60,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6091,6091,Matières premières,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6092,6092,Matières consommables,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6093,6093,Fournitures consommables,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6094,6094,Emballages,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6095,6095,Approvisionnements,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6096,6096,Achats de biens destinés à la revente,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6098,6098,Achats non stockés et achats incorporés aux ouvrages et produits,lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6099,6099,"Rabais, remises et ristournes non affectés",lu_2011_account_609,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61,61,Autres charges externes,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f,account_financial_report_121 +lu_2011_account_611,611,Loyers et charges locatives,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6111,6111,Locations immobilières,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61111,61111,Terrains,lu_2011_account_6111,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61112,61112,Bâtiments,lu_2011_account_6111,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6112,6112,Locations mobilières,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61121,61121,Installations techniques et machines,lu_2011_account_6112,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61122,61122,"Autres installations, outillages et machines",lu_2011_account_6112,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61123,61123,Matériel roulant,lu_2011_account_6112,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6113,6113,Charges locatives et de copropriété,lu_2011_account_611,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6114,6114,Leasing immobilier,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61141,61141,Terrains,lu_2011_account_6114,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61142,61142,Bâtiments,lu_2011_account_6114,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6115,6115,Leasing mobilier,lu_2011_account_611,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61151,61151,Installations techniques et machines,lu_2011_account_6115,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61152,61152,"Autres installations, outillages et machines",lu_2011_account_6115,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61153,61153,Matériel roulant,lu_2011_account_6115,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6116,6116,Malis sur emballages,lu_2011_account_611,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_612,612,"Sous-traitance, entretiens et réparations",lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6121,6121,"Sous-traitance générale (non incorporée directement aux ouvrages, travaux et produits)",lu_2011_account_612,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6122,6122,Entretien et réparations,lu_2011_account_612,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61221,61221,Sur installations techniques et machines,lu_2011_account_6122,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61222,61222,"Sur autres installations, outillages et machines",lu_2011_account_6122,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61223,61223,Sur matériel roulant,lu_2011_account_6122,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6123,6123,Contrats de maintenance,lu_2011_account_612,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6124,6124,Etudes et recherches (non incorporées dans les produits),lu_2011_account_612,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_613,613,Rémunérations d'intermédiaires et honoraires,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6131,6131,Commissions et courtages,lu_2011_account_613,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61311,61311,Commissions et courtages sur achats,lu_2011_account_6131,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61312,61312,Commissions et courtages sur ventes,lu_2011_account_6131,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61313,61313,Rémunérations des transitaires,lu_2011_account_6131,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6132,6132,Traitement informatique,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6133,6133,Services bancaires et assimilés,lu_2011_account_613,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61331,61331,"Frais sur titres (achat, vente, garde)",lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61332,61332,Commissions et frais sur émission d'emprunts,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61333,61333,Frais de compte,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61334,61334,Frais sur cartes de crédit,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61335,61335,Frais sur effets,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61336,61336,Rémunérations d'affacturage,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61337,61337,Location de coffres,lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61338,61338,Autres frais et commissions bancaires (hors intérêts et frais assimilés),lu_2011_account_6133,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6134,6134,Honoraires,lu_2011_account_613,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61341,61341,Honoraires juridiques,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61342,61342,Honoraires comptables et d'audit,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61343,61343,Honoraires fiscaux,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61348,61348,Autres honoraires,lu_2011_account_6134,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6135,6135,Frais d'actes et de contentieux,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6136,6136,Frais de recrutement de personnel,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6138,6138,Autres rémunérations d'intermédiaires et honoraires,lu_2011_account_613,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_614,614,Primes d'assurance,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6141,6141,Assurances sur biens de l'actif,lu_2011_account_614,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61411,61411,Bâtiments,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61412,61412,Véhicules,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61413,61413,Installations,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61418,61418,Sur autres biens de l'actif,lu_2011_account_6141,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6142,6142,Assurances sur biens pris en location,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6143,6143,Assurance-transport,lu_2011_account_614,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61431,61431,sur achats,lu_2011_account_6143,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61432,61432,sur ventes,lu_2011_account_6143,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61438,61438,sur autres biens,lu_2011_account_6143,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6144,6144,Assurance risque d'exploitation,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6145,6145,Assurance insolvabilité clients,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6146,6146,Assurance responsabilité civile,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6148,6148,Autres assurances,lu_2011_account_614,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_615,615,Frais de marketing et de communication,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6151,6151,Frais de marketing et de publicité,lu_2011_account_615,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61511,61511,Annonces et insertions,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61512,61512,Echantillons,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61513,61513,Foires et expositions,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61514,61514,Cadeaux à la clientèle,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61515,61515,Catalogues et imprimés et publications,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61516,61516,Dons courants,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61517,61517,Sponsoring,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61518,61518,Autres achats de services publicitaires,lu_2011_account_6151,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6152,6152,Frais de déplacements et de représentation,lu_2011_account_615,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61521,61521,Voyages et déplacements,lu_2011_account_6152,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_615211,615211,Direction (respectivement exploitant et associés),lu_2011_account_61521,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_615212,615212,Personnel,lu_2011_account_61521,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61522,61522,Frais de déménagement de l'entreprise,lu_2011_account_6152,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61523,61523,Missions,lu_2011_account_6152,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61524,61524,Réceptions et frais de représentation,lu_2011_account_6152,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6153,6153,Frais postaux et frais de télécommunications,lu_2011_account_615,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61531,61531,Timbres,lu_2011_account_6153,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61532,61532,Téléphone et autres frais de télécommunication,lu_2011_account_6153,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61538,61538,"Autres frais postaux (location de boîtes postales, etc.)",lu_2011_account_6153,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_616,616,Transports de biens et transports collectifs du personnel,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6161,6161,Transports sur achats,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6162,6162,Transports sur ventes,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6163,6163,Transports entre établissements ou chantiers,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6164,6164,Transports administratifs,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6165,6165,Transports collectifs du personnel,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6168,6168,Autres transports,lu_2011_account_616,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_617,617,Personnel extérieur à l'entreprise,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6171,6171,Personnel intérimaire,lu_2011_account_617,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6172,6172,Personnel prêté à l'entreprise,lu_2011_account_617,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_618,618,Charges externes diverses,lu_2011_account_61,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6181,6181,Documentation,lu_2011_account_618,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61811,61811,Documentation générale,lu_2011_account_6181,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_61812,61812,Documentation technique,lu_2011_account_6181,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6182,6182,"Frais de colloques, séminaires, conférences",lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6183,6183,Elimination des déchets industriels,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6184,6184,Elimination de déchets non industriels,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6185,6185,Evacuation des eaux usées,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6186,6186,Frais de surveillance,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6187,6187,Cotisations aux associations professionnelles,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6188,6188,Autres charges externes diverses,lu_2011_account_618,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_619,619,"Rabais, remises et ristournes obtenus sur autres charges externes",lu_2011_account_61,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62,62,Frais de personnel,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_621,621,Rémunérations des salariés,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,account_financial_report_123 +lu_2011_account_6211,6211,Salaires bruts,lu_2011_account_621,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62111,62111,Salaires de base,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62112,62112,Suppléments pour travail,lu_2011_account_6211,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_621121,621121,Dimanche,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_621122,621122,Jours fériés légaux,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_621123,621123,Heures supplémentaires,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_621128,621128,Autres suppléments,lu_2011_account_62112,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62113,62113,Primes de ménage,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62114,62114,"Gratifications, primes et commissions",lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62115,62115,Avantages en nature,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62116,62116,Indemnités de licenciement,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62117,62117,Trimestre de faveur,lu_2011_account_6211,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6218,6218,Autres avantages,lu_2011_account_621,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6219,6219,Remboursements sur salaires,lu_2011_account_621,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62191,62191,Remboursements mutualité,lu_2011_account_6219,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62192,62192,"Remboursements pour congé politique, sportif, culturel, éducatif et mandats sociaux",lu_2011_account_6219,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62193,62193,Remboursements trimestre de faveur,lu_2011_account_6219,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_622,622,Autre personnel,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,account_financial_report_123 +lu_2011_account_6221,6221,Etudiants,lu_2011_account_622,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6222,6222,Salaires occasionnels,lu_2011_account_622,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6228,6228,Autre personnel temporaire,lu_2011_account_622,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_623,623,Charges sociales (part patronale),lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,account_financial_report_124 +lu_2011_account_6231,6231,Charges sociales salariés,lu_2011_account_623,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62311,62311,Caisse Nationale de Santé,lu_2011_account_6231,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62312,62312,Caisse Nationale d'Assurance-Pension,lu_2011_account_6231,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_62318,62318,Cotisations patronales complémentaires,lu_2011_account_6231,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6232,6232,Assurance accidents du travail,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6233,6233,Service de santé au travail,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6238,6238,Autres charges sociales patronales,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6239,6239,Remboursements de charges sociales,lu_2011_account_623,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_624,624,Pensions complémentaires,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,account_financial_report_125 +lu_2011_account_6241,6241,Primes à des fonds de pensions extérieurs,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6242,6242,Dotation aux provisions pour pensions complémentaires,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6243,6243,Retenue d'impôt sur pension complémentaire,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6244,6244,Prime d'assurance insolvabilité,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6245,6245,Pensions complémentaires versées par l'employeur,lu_2011_account_624,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_628,628,Autres charges sociales,lu_2011_account_62,view,account_type_2011_6_charge_exploitation,f,account_financial_report_126 +lu_2011_account_6281,6281,Médecine du travail,lu_2011_account_628,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6288,6288,Autres charges sociales diverses,lu_2011_account_628,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_63,63,Dotations aux corrections de valeur des éléments d'actif non financiers,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_631,631,Dotations aux corrections de valeur sur frais d'établissement et frais assimilés,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,account_financial_report_128 +lu_2011_account_6311,6311,Frais de constitution,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6312,6312,Frais de premier établissement,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6313,6313,Frais d'augmentation de capital et d'opérations diverses,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6314,6314,Frais d'émission d'emprunts,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6318,6318,Autres frais assimilés,lu_2011_account_631,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_632,632,Dotations aux corrections de valeur sur immobilisations incorporelles,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,account_financial_report_128 +lu_2011_account_6321,6321,Frais de recherche et de développement,lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6322,6322,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6323,6323,Fonds de commerce dans la mesure où il a été acquis à titre onéreux,lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6324,6324,Acomptes versés et immobilisations incorporelles en cours,lu_2011_account_632,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_633,633,Dotations aux corrections de valeur sur immobilisations corporelles,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,account_financial_report_128 +lu_2011_account_6331,6331,Terrains et constructions,lu_2011_account_633,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_63311,63311,Terrains,lu_2011_account_6331,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_63312,63312,Agencements et aménagements de terrains,lu_2011_account_6331,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_63313,63313,Constructions,lu_2011_account_6331,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6332,6332,Installations techniques et machines,lu_2011_account_633,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6333,6333,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_633,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6334,6334,Acomptes versés et immobilisations corporelles en cours,lu_2011_account_633,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_634,634,Dotations aux corrections de valeur sur stocks,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,account_financial_report_129 +lu_2011_account_6341,6341,Matières premières et consommables,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6342,6342,Produits en cours de fabrication et commandes en cours,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6343,6343,Produits finis et marchandises,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6344,6344,Terrains et immeubles destinés à la revente,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6345,6345,Acomptes versés,lu_2011_account_634,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_635,635,Dotations aux corrections de valeur sur créances de l'actif circulant,lu_2011_account_63,view,account_type_2011_6_charge_exploitation,f,account_financial_report_129 +lu_2011_account_6351,6351,Créances résultant de ventes et prestations de services,lu_2011_account_635,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6352,6352,Créances sur des entreprises liées et des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_635,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6353,6353,Autres créances,lu_2011_account_635,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64,64,Autres charges d'exploitation,lu_2011_account_6,view,account_type_2011_6_charge_exploitation,f,account_financial_report_130 +lu_2011_account_641,641,"Redevances pour concessions, brevets, licences, marques, droits et valeurs similaires",lu_2011_account_64,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6411,6411,Concessions,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6412,6412,Brevets,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6413,6413,Licences informatiques,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6414,6414,Marques et franchises,lu_2011_account_641,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6415,6415,Droits et valeurs similaires,lu_2011_account_641,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64151,64151,Droits d'auteur et de reproduction,lu_2011_account_6415,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64158,64158,Autres droits et valeurs similaires,lu_2011_account_6415,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_642,642,Indemnités,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_643,643,Jetons de présence,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_644,644,Tantièmes,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_645,645,Pertes sur créances irrécouvrables,lu_2011_account_64,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6451,6451,Créances résultant de ventes et de prestations de services,lu_2011_account_645,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6452,6452,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_645,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6453,6453,Autres créances,lu_2011_account_645,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_646,646,"Impôts, taxes et versements assimilés",lu_2011_account_64,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6461,6461,Impôt foncier,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6462,6462,TVA non déductible,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6463,6463,Droits sur les marchandises en provenance de l'étranger,lu_2011_account_646,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64631,64631,Droits d'accises et taxe de consommation sur marchandises en provenance de l'étranger,lu_2011_account_6463,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64632,64632,Droits de douane,lu_2011_account_6463,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64633,64633,Montants compensatoires,lu_2011_account_6463,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6464,6464,Droits d'accises à la production et taxe de consommation,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6465,6465,"Droits d'enregistrement et de timbre, droits d'hypothèques",lu_2011_account_646,view,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64651,64651,Droits d'enregistrement,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64652,64652,Taxe d'abonnement,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64653,64653,Droits d'hypothèques,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64654,64654,Droits de timbre,lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_64658,64658,"Autres droits d'enregistrement et de timbre, droits d'hypothèques",lu_2011_account_6465,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6466,6466,Taxes sur les véhicules,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6467,6467,Taxe de cabaretage,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6468,6468,Autres droits et impôts,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_6469,6469,Dotations aux provisions pour impôts,lu_2011_account_646,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_647,647,Dotations aux plus-values immunisées,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_648,648,Autres charges d'exploitation diverses,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_649,649,Dotations aux provisions d'exploitation,lu_2011_account_64,other,account_type_2011_6_charge_exploitation,f, +lu_2011_account_65,65,Charges financières,lu_2011_account_6,view,account_type_2011_6_charge_finance,f, +lu_2011_account_651,651,Dotations aux corrections de valeur et ajustements pour juste valeur sur immobilisations financières,lu_2011_account_65,view,account_type_2011_6_charge_finance,f,account_financial_report_131 +lu_2011_account_6511,6511,Dotations aux corrections de valeur sur immobilisations financières,lu_2011_account_651,view,account_type_2011_6_charge_finance,f, +lu_2011_account_65111,65111,Parts dans des entreprises liées,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65112,65112,Créances sur des entreprises liées,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65113,65113,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65114,65114,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65115,65115,Titres ayant le caractère d'immobilisations,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65116,65116,Prêts et créances immobilisées,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65117,65117,Actions propres ou parts propres,lu_2011_account_6511,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6512,6512,Ajustements pour juste valeur sur immobilisations financières,lu_2011_account_651,other,account_type_2011_6_charge_finance,f, +lu_2011_account_653,653,Dotations aux corrections de valeur et ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_65,view,account_type_2011_6_charge_finance,f,account_financial_report_132 +lu_2011_account_6531,6531,Dotations aux corrections de valeur sur valeurs mobilières,lu_2011_account_653,view,account_type_2011_6_charge_finance,f, +lu_2011_account_65311,65311,Parts dans des entreprises liées,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65312,65312,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65313,65313,Actions propres ou parts propres,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65318,65318,Autres valeurs mobilières,lu_2011_account_6531,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6532,6532,Dotations aux corrections de valeur sur créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_653,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6533,6533,Dotations aux corrections de valeur sur autres créances,lu_2011_account_653,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6534,6534,Ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_653,other,account_type_2011_6_charge_finance,f, +lu_2011_account_654,654,Moins-values de cession de valeurs mobilières,lu_2011_account_65,view,account_type_2011_6_charge_finance,f,account_financial_report_135 +lu_2011_account_6541,6541,Parts dans des entreprises liées,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6542,6542,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6543,6543,Actions propres ou parts propres,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6548,6548,Autres valeurs mobilières,lu_2011_account_654,other,account_type_2011_6_charge_finance,f, +lu_2011_account_655,655,Intérêts et escomptes,lu_2011_account_65,view,account_type_2011_6_charge_finance,f,account_financial_report_135 +lu_2011_account_6551,6551,Intérêts des dettes financières,lu_2011_account_655,view,account_type_2011_6_charge_finance,f, +lu_2011_account_65511,65511,Intérêts des dettes subordonnées,lu_2011_account_6551,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65512,65512,Intérêts des emprunts obligataires,lu_2011_account_6551,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6552,6552,Intérêts bancaires et assimilés,lu_2011_account_655,view,account_type_2011_6_charge_finance,f, +lu_2011_account_65521,65521,Intérêts bancaires sur comptes courants,lu_2011_account_6552,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65522,65522,Intérêts bancaires sur opérations de financement,lu_2011_account_6552,other,account_type_2011_6_charge_finance,f, +lu_2011_account_65523,65523,Intérêts sur leasings financiers,lu_2011_account_6552,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6553,6553,Intérêts sur dettes commerciales,lu_2011_account_655,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6554,6554,Intérêts sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_655,other,account_type_2011_6_charge_finance,f,account_financial_report_134 +lu_2011_account_6555,6555,Escomptes et frais sur effets,lu_2011_account_655,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6556,6556,Escomptes accordés,lu_2011_account_655,other,account_type_2011_6_charge_finance,f, +lu_2011_account_6558,6558,Intérêts sur autres emprunts et dettes,lu_2011_account_655,other,account_type_2011_6_charge_finance,f, +lu_2011_account_656,656,Pertes de change,lu_2011_account_65,other,account_type_2011_6_charge_finance,f,account_financial_report_135 +lu_2011_account_657,657,Quote-part de perte dans les entreprises collectives (autres que les sociétés de capitaux),lu_2011_account_65,other,account_type_2011_6_charge_finance,f,account_financial_report_135 +lu_2011_account_658,658,Autres charges financières,lu_2011_account_65,other,account_type_2011_6_charge_finance,f,account_financial_report_135 +lu_2011_account_659,659,Dotations aux provisions financières,lu_2011_account_65,other,account_type_2011_6_charge_finance,f,account_financial_report_135 +lu_2011_account_66,66,Charges exceptionnelles,lu_2011_account_6,view,account_type_2011_6_charge_exception,f,account_financial_report_136 +lu_2011_account_661,661,Dotations aux corrections de valeur exceptionnelles sur immobilisations incorporelles et corporelles,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, +lu_2011_account_6611,6611,Sur immobilisations incorporelles,lu_2011_account_661,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6612,6612,Sur immobilisations corporelles,lu_2011_account_661,other,account_type_2011_6_charge_exception,f, +lu_2011_account_662,662,Dotations aux corrections de valeur exceptionnelles sur éléments de l'actif circulant,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, +lu_2011_account_6621,6621,Sur stocks,lu_2011_account_662,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6622,6622,Sur créances,lu_2011_account_662,other,account_type_2011_6_charge_exception,f, +lu_2011_account_663,663,Valeur comptable des immobilisations incorporelles et corporelles cédées,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, +lu_2011_account_6631,6631,Immobilisations incorporelles,lu_2011_account_663,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6632,6632,Immobilisations corporelles,lu_2011_account_663,other,account_type_2011_6_charge_exception,f, +lu_2011_account_664,664,Valeur comptable des immobilisations financières cédées,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, +lu_2011_account_6641,6641,Parts dans des entreprises liées,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6642,6642,Créances sur des entreprises liées,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6643,6643,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6644,6644,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6645,6645,Titres ayant le caractère d'immobilisations,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6646,6646,Prêts et créances immobilisées,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6647,6647,Actions propres ou parts propres,lu_2011_account_664,other,account_type_2011_6_charge_exception,f, +lu_2011_account_665,665,Valeur comptable des créances de l'actif circulant financier cédées,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, +lu_2011_account_6651,6651,Sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_665,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6652,6652,Sur autres créances,lu_2011_account_665,other,account_type_2011_6_charge_exception,f, +lu_2011_account_668,668,Autres charges exceptionnelles,lu_2011_account_66,view,account_type_2011_6_charge_exception,f, +lu_2011_account_6681,6681,Pénalités sur marchés et dédits payés sur achats et ventes,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6682,6682,"Amendes et pénalités fiscales, sociales et pénales",lu_2011_account_668,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6683,6683,Dommages et intérêts,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6684,6684,Malis provenant de clauses d'indexation,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, +lu_2011_account_6688,6688,Autres charges exceptionnelles diverses,lu_2011_account_668,other,account_type_2011_6_charge_exception,f, +lu_2011_account_669,669,Dotations aux provisions exceptionnelles,lu_2011_account_66,other,account_type_2011_6_charge_exception,f, +lu_2011_account_67,67,Impôts sur le résultat,lu_2011_account_6,view,account_type_2011_6_charge_impot,f,account_financial_report_137 +lu_2011_account_671,671,Impôt sur le revenu des collectivités,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, +lu_2011_account_6711,6711,Exercice courant,lu_2011_account_671,other,account_type_2011_6_charge_impot,f, +lu_2011_account_6712,6712,Exercices antérieurs,lu_2011_account_671,other,account_type_2011_6_charge_impot,f, +lu_2011_account_672,672,Impôt commercial,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, +lu_2011_account_6721,6721,Exercice courant,lu_2011_account_672,other,account_type_2011_6_charge_impot,f, +lu_2011_account_6722,6722,Exercices antérieurs,lu_2011_account_672,other,account_type_2011_6_charge_impot,f, +lu_2011_account_673,673,Impôts étrangers sur le résultat,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, +lu_2011_account_6731,6731,Retenues d'impôt à la source,lu_2011_account_673,other,account_type_2011_6_charge_impot,f, +lu_2011_account_6732,6732,Impôts supportés par les établissements stables,lu_2011_account_673,view,account_type_2011_6_charge_impot,f, +lu_2011_account_67321,67321,Exercice courant,lu_2011_account_6732,other,account_type_2011_6_charge_impot,f, +lu_2011_account_67322,67322,Exercices antérieurs,lu_2011_account_6732,other,account_type_2011_6_charge_impot,f, +lu_2011_account_6733,6733,Impôts supportés par les entreprises non résidentes,lu_2011_account_673,other,account_type_2011_6_charge_impot,f, +lu_2011_account_6738,6738,Autres impôts étrangers,lu_2011_account_673,other,account_type_2011_6_charge_impot,f, +lu_2011_account_679,679,Dotations aux provisions pour impôts sur le résultat,lu_2011_account_67,view,account_type_2011_6_charge_impot,f, +lu_2011_account_6791,6791,Dotations aux provisions pour impôts,lu_2011_account_679,other,account_type_2011_6_charge_impot,f, +lu_2011_account_6792,6792,Dotations aux provisions pour impôts différés,lu_2011_account_679,other,account_type_2011_6_charge_impot,f, +lu_2011_account_68,68,Autres impôts ne figurant pas sous le poste ci-dessus,lu_2011_account_6,view,account_type_2011_6_charge_impot,f,account_financial_report_138 +lu_2011_account_681,681,Impôt sur la fortune,lu_2011_account_68,view,account_type_2011_6_charge_impot,f, +lu_2011_account_6811,6811,Exercice courant,lu_2011_account_681,other,account_type_2011_6_charge_impot,f, +lu_2011_account_6812,6812,Exercices antérieurs,lu_2011_account_681,other,account_type_2011_6_charge_impot,f, +lu_2011_account_682,682,Taxe d'abonnement,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, +lu_2011_account_683,683,Impôts étrangers,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, +lu_2011_account_688,688,Autres impôts et taxes,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, +lu_2011_account_689,689,Dotations aux provisions pour autres impôts,lu_2011_account_68,other,account_type_2011_6_charge_impot,f, +lu_2011_account_7,7,CLASSE 7 - COMPTES DE PRODUITS,lu_2011_account_resultat,view,account.data_account_type_view,f, +lu_2011_account_70,70,Montant net du chiffre d'affaires,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,account_financial_report_141 +lu_2011_account_701,701,Ventes sur commandes en cours,lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7011,7011,Produits,lu_2011_account_701,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7012,7012,Prestations de services,lu_2011_account_701,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7013,7013,Immeubles en construction,lu_2011_account_701,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_702,702,Ventes de produits finis,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_703,703,Ventes de produits intermédiaires,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_704,704,Ventes de produits résiduels,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_705,705,Ventes d'éléments destinés à la revente,lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7051,7051,Ventes de marchandises,lu_2011_account_705,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7052,7052,Ventes de terrains et d'immeubles existants (promotion immobilière),lu_2011_account_705,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7053,7053,Ventes d'autres éléments destinés à la revente,lu_2011_account_705,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_706,706,Prestations de services,lu_2011_account_70,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_708,708,Autres éléments du chiffre d'affaires,lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7081,7081,Commissions et courtages,lu_2011_account_708,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7082,7082,Locations,lu_2011_account_708,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_70821,70821,Loyer immobilier,lu_2011_account_7082,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_70822,70822,Loyer mobilier,lu_2011_account_7082,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7083,7083,Ventes d'emballages,lu_2011_account_708,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7088,7088,Autres éléments divers du chiffre d'affaires,lu_2011_account_708,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_709,709,"Rabais, remises et ristournes accordés par l'entreprise",lu_2011_account_70,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7091,7091,Sur ventes sur commandes en cours,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7092,7092,Sur ventes de produits finis,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7093,7093,Sur ventes de produits intermédiaires,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7094,7094,Sur ventes de produits résiduels,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7095,7095,Sur ventes d'éléments destinés à la revente,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7096,7096,Sur prestations de services,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7098,7098,Sur autres éléments du chiffre d'affaires,lu_2011_account_709,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_71,71,"Variation des stocks de produits finis, d'en cours de fabrication et des commandes en cours",lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,account_financial_report_142 +lu_2011_account_711,711,Variation des stocks de produits en cours de fabrication et de commandes en cours,lu_2011_account_71,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7111,7111,Variation des stocks de produits en cours,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7112,7112,Variation des stocks de commandes en cours – produits,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7113,7113,Variation des stocks de commandes en cours – prestations de services,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7114,7114,Variation des stocks d'immeubles en construction,lu_2011_account_711,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_712,712,Variation des stocks de produits finis et marchandises,lu_2011_account_71,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7121,7121,Variation des stocks de produits finis,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7122,7122,Variation des stocks de produits intermédiaires,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7123,7123,Variation des stocks de produits résiduels,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7126,7126,Variation des stocks de marchandises,lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7127,7127,"Variation des stocks de marchandises en voie d'acheminement, mises en dépôt ou données en consignation",lu_2011_account_712,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_72,72,Production immobilisée,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,account_financial_report_143 +lu_2011_account_721,721,Immobilisations incorporelles,lu_2011_account_72,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7211,7211,Frais de recherche et développement,lu_2011_account_721,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7212,7212,"Concessions, brevets, licences, marques, droits et valeurs similaires",lu_2011_account_721,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_72121,72121,Concessions,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_72122,72122,Brevets,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_72123,72123,Licences informatiques,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_72124,72124,Marques et franchises,lu_2011_account_7212,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_72125,72125,Droits et valeurs similaires,lu_2011_account_7212,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_721251,721251,Droits d'auteur et de reproduction,lu_2011_account_72125,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_721258,721258,Autres droits et valeurs similaires,lu_2011_account_72125,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_722,722,Immobilisations corporelles,lu_2011_account_72,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7221,7221,Terrains et constructions,lu_2011_account_722,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7222,7222,Installations techniques et machines,lu_2011_account_722,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7223,7223,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_722,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_73,73,Reprises de corrections de valeur des éléments d'actif non financiers,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_732,732,Reprises de corrections de valeur sur immobilisations incorporelles,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,account_financial_report_145 +lu_2011_account_7321,7321,Frais de recherche et de développement,lu_2011_account_732,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7322,7322,"Concessions, brevets, licences, marques ainsi que droits et valeurs similaires",lu_2011_account_732,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7323,7323,"Fonds de commerce, dans la mesure où il a été acquis à titre onéreux",lu_2011_account_732,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7324,7324,Acomptes versés et immobilisations incorporelles en cours,lu_2011_account_732,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_733,733,Reprises de corrections de valeur sur immobilisations corporelles,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,account_financial_report_145 +lu_2011_account_7331,7331,Terrains et constructions,lu_2011_account_733,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_73311,73311,Terrains,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_73312,73312,Agencements et aménagements de terrains,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_73313,73313,Constructions,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_73314,73314,Constructions sur sol d'autrui,lu_2011_account_7331,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7332,7332,Installations techniques et machines,lu_2011_account_733,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7333,7333,"Autres installations, outillage, mobilier et matériel roulant",lu_2011_account_733,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7334,7334,Acomptes versés et immobilisations corporelles en cours,lu_2011_account_733,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_734,734,Reprises de corrections de valeur sur stocks,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,account_financial_report_146 +lu_2011_account_7341,7341,Matières premières et consommables,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7342,7342,Produits en cours de fabrication et commandes en cours,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7343,7343,Produits finis et marchandises,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7344,7344,Terrains et immeubles destinés à la revente,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7345,7345,Acomptes versés,lu_2011_account_734,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_735,735,Reprises de corrections de valeur sur créances de l'actif circulant,lu_2011_account_73,view,account_type_2011_7_produit_exploitation,f,account_financial_report_146 +lu_2011_account_7351,7351,Créances résultant de ventes et prestations de services,lu_2011_account_735,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7352,7352,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_735,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7353,7353,Autres créances,lu_2011_account_735,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_74,74,Autres produits d'exploitation,lu_2011_account_7,view,account_type_2011_7_produit_exploitation,f,account_financial_report_147 +lu_2011_account_741,741,"Redevances pour concessions, brevets, licences, marques, droits et valeurs similaires",lu_2011_account_74,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7411,7411,Concessions,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7412,7412,Brevets,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7413,7413,Licences informatiques,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7414,7414,Marques et franchises,lu_2011_account_741,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7415,7415,Droits et valeurs similaires,lu_2011_account_741,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_74151,74151,Droits d'auteur et de reproduction,lu_2011_account_7415,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_74158,74158,Autres droits et valeurs similaires,lu_2011_account_7415,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_742,742,Revenus des immeubles non affectés aux activités professionnelles,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_743,743,"Jetons de présence, tantièmes et rémunérations assimilées",lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_744,744,Subventions d'exploitation,lu_2011_account_74,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7441,7441,Subventions sur produits,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7442,7442,Bonifications d'intérêt,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7443,7443,Montants compensatoires,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7444,7444,Subventions destinées à promouvoir l'emploi,lu_2011_account_744,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_74441,74441,Primes d'apprentissage reçues,lu_2011_account_7444,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_74442,74442,Autres subventions destinées à promouvoir l'emploi,lu_2011_account_7444,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7448,7448,Autres subventions d'exploitation,lu_2011_account_744,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_745,745,Ristournes perçues des coopératives (provenant des excédents),lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_746,746,Indemnités d'assurance touchées,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_747,747,Reprises de plus-values immunisées et de subventions d'investissement en capital,lu_2011_account_74,view,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7471,7471,Plus-values immunisées non réinvesties,lu_2011_account_747,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7472,7472,Plus-values immunisées réinvesties,lu_2011_account_747,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_7473,7473,Subventions d'investissement en capital,lu_2011_account_747,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_748,748,Autres produits d'exploitation divers,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_749,749,Reprises sur provisions d'exploitation,lu_2011_account_74,other,account_type_2011_7_produit_exploitation,f, +lu_2011_account_75,75,Produits financiers,lu_2011_account_7,view,account_type_2011_7_produit_financier,f, +lu_2011_account_751,751,Reprises sur corrections de valeur et ajustements pour juste valeur sur immobilisations financières,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, +lu_2011_account_7511,7511,Reprises sur corrections de valeur sur immobilisations financières,lu_2011_account_751,view,account_type_2011_7_produit_financier,f, +lu_2011_account_75111,75111,Parts dans des entreprises liées,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,account_financial_report_149 +lu_2011_account_75112,75112,Créances sur des entreprises liées,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,account_financial_report_149 +lu_2011_account_75113,75113,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_75114,75114,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_75115,75115,Titres ayant le caractère d'immobilisations,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_75116,75116,Prêts et créances immobilisées,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_75117,75117,Actions propres ou parts propres,lu_2011_account_7511,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_7512,7512,Ajustements pour juste valeur sur immobilisations financières,lu_2011_account_751,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_752,752,Revenus des immobilisations financières,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, +lu_2011_account_7521,7521,Parts dans des entreprises liées,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,account_financial_report_149 +lu_2011_account_7522,7522,Créances sur des entreprises liées,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,account_financial_report_149 +lu_2011_account_7523,7523,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_7524,7524,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_7525,7525,Titres ayant le caractère d'immobilisations,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_7526,7526,Prêts et créances immobilisées,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_7527,7527,Actions propres ou parts propres,lu_2011_account_752,other,account_type_2011_7_produit_financier,f,account_financial_report_150 +lu_2011_account_753,753,Reprises sur corrections de valeur et ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, +lu_2011_account_7531,7531,Reprises sur corrections de valeur sur créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_753,other,account_type_2011_7_produit_financier,f,account_financial_report_152 +lu_2011_account_7532,7532,Reprises sur corrections de valeur sur autres créances,lu_2011_account_753,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_7533,7533,Reprises sur corrections de valeur sur valeurs mobilières,lu_2011_account_753,view,account_type_2011_7_produit_financier,f, +lu_2011_account_75331,75331,Parts dans des entreprises liées,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,account_financial_report_152 +lu_2011_account_75332,75332,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_75333,75333,Actions propres ou parts propres,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_75338,75338,Autres valeurs mobilières,lu_2011_account_7533,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_7534,7534,Ajustements pour juste valeur sur éléments financiers de l'actif circulant,lu_2011_account_753,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_754,754,Plus-value de cession et autres produits de valeurs mobilières,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, +lu_2011_account_7541,7541,Plus-value de cession de valeurs mobilières,lu_2011_account_754,view,account_type_2011_7_produit_financier,f, +lu_2011_account_75411,75411,Parts dans des entreprises liées,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,account_financial_report_152 +lu_2011_account_75412,75412,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_75413,75413,Actions propres ou parts propres,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_75418,75418,Autres valeurs mobilières,lu_2011_account_7541,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_7548,7548,Autres produits de valeurs mobilières,lu_2011_account_754,view,account_type_2011_7_produit_financier,f, +lu_2011_account_75481,75481,Parts dans des entreprises liées,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,account_financial_report_152 +lu_2011_account_75482,75482,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_75483,75483,Actions propres ou parts propres,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_75488,75488,Autres valeurs mobilières,lu_2011_account_7548,other,account_type_2011_7_produit_financier,f,account_financial_report_153 +lu_2011_account_755,755,Autres intérêts et escomptes,lu_2011_account_75,view,account_type_2011_7_produit_financier,f, +lu_2011_account_7552,7552,Intérêts bancaires et assimilés,lu_2011_account_755,view,account_type_2011_7_produit_financier,f,account_financial_report_156 +lu_2011_account_75521,75521,Intérêts sur comptes courants,lu_2011_account_7552,other,account_type_2011_7_produit_financier,f, +lu_2011_account_75522,75522,Intérêts sur comptes à terme,lu_2011_account_7552,other,account_type_2011_7_produit_financier,f, +lu_2011_account_75523,75523,Intérêts sur leasings financiers,lu_2011_account_7552,other,account_type_2011_7_produit_financier,f, +lu_2011_account_7553,7553,Intérêts sur créances commerciales,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,account_financial_report_156 +lu_2011_account_7554,7554,Intérêts sur des entreprises liées et des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,account_financial_report_155 +lu_2011_account_7555,7555,Escomptes d'effets de commerce,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,account_financial_report_156 +lu_2011_account_7556,7556,Escomptes obtenus,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,account_financial_report_156 +lu_2011_account_7558,7558,Intérêts sur autres créances,lu_2011_account_755,other,account_type_2011_7_produit_financier,f,account_financial_report_156 +lu_2011_account_756,756,Gains de change,lu_2011_account_75,other,account_type_2011_7_produit_financier,f, +lu_2011_account_757,757,Quote-part de bénéfice dans les entreprises collectives (autres que les sociétés de capitaux),lu_2011_account_75,other,account_type_2011_7_produit_financier,f, +lu_2011_account_758,758,Autres produits financiers,lu_2011_account_75,other,account_type_2011_7_produit_financier,f,account_financial_report_156 +lu_2011_account_759,759,Reprises sur provisions financières,lu_2011_account_75,other,account_type_2011_7_produit_financier,f, +lu_2011_account_76,76,Produits exceptionnels,lu_2011_account_7,view,account_type_2011_7_produit_exceptionnel,f,account_financial_report_157 +lu_2011_account_761,761,Reprises sur corrections de valeur exceptionnelles sur immobilisations incorporelles et corporelles,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7611,7611,Immobilisations incorporelles,lu_2011_account_761,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7612,7612,Immobilisations corporelles,lu_2011_account_761,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_762,762,Reprises sur corrections de valeur exceptionnelles sur éléments de l'actif circulant,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7621,7621,Sur stocks,lu_2011_account_762,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7622,7622,Sur créances de l'actif circulant,lu_2011_account_762,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_763,763,Produits de cession d'immobilisations incorporelles et corporelles,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7631,7631,Immobilisations incorporelles,lu_2011_account_763,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7632,7632,Immobilisations corporelles,lu_2011_account_763,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_764,764,Produits de cession d'immobilisations financières,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7641,7641,Parts dans des entreprises liées,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7642,7642,Créances sur des entreprises liées,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7643,7643,Parts dans des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7644,7644,Créances sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7645,7645,Titres ayant le caractère d'immobilisations,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7646,7646,Prêts et créances immobilisés,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7647,7647,Actions propres ou parts propres,lu_2011_account_764,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_765,765,Produits de cession sur créances de l'actif circulant financier,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7651,7651,Créances sur des entreprises liées et sur des entreprises avec lesquelles la société a un lien de participation,lu_2011_account_765,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7652,7652,Autres créances,lu_2011_account_765,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_768,768,Autres produits exceptionnels,lu_2011_account_76,view,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7681,7681,Pénalités sur marchés et dédits perçus sur achats et sur ventes,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7682,7682,Libéralités reçues,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7683,7683,Rentrées sur créances amorties,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7684,7684,Subventions exceptionnelles,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7685,7685,Bonis provenant de clauses d'indexation,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7686,7686,Bonis provenant du rachat par l'entreprise d'actions et d'obligations émises par elle-même,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7688,7688,Autres produits exceptionnels divers,lu_2011_account_768,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_769,769,Reprises sur provisions exceptionnelles,lu_2011_account_76,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_77,77,Régularisations d'impôts sur le résultat,lu_2011_account_7,view,account_type_2011_7_produit_exceptionnel,f,account_financial_report_137 +lu_2011_account_771,771,Régularisations d'impôt sur le revenu des collectivités,lu_2011_account_77,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_772,772,Régularisations d'impôt commercial,lu_2011_account_77,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_773,773,Régularisations d'impôts étrangers sur le résultat,lu_2011_account_77,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_779,779,Reprises sur provisions pour impôts sur le résultat,lu_2011_account_77,view,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7791,7791,Reprises sur provisions pour impôts,lu_2011_account_779,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_7792,7792,Reprises sur provisions pour impôts différés,lu_2011_account_779,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_78,78,Régularisations d'autres impôts ne figurant pas sous le poste ci-dessus,lu_2011_account_7,view,account_type_2011_7_produit_exceptionnel,f,account_financial_report_138 +lu_2011_account_781,781,Régularisations d'impôt sur la fortune,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_782,782,Régularisations de taxes d'abonnement,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_783,783,Régularisations d'impôts étrangers,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_788,788,Régularisations d'autres impôts et taxes,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, +lu_2011_account_789,789,Reprises sur provisions pour autres impôts,lu_2011_account_78,other,account_type_2011_7_produit_exceptionnel,f, diff --git a/addons/l10n_lu/account.tax.template-2011.csv b/addons/l10n_lu/account.tax.template-2011.csv index a4db712581ef0..4aaf2eca0a6fa 100644 --- a/addons/l10n_lu/account.tax.template-2011.csv +++ b/addons/l10n_lu/account.tax.template-2011.csv @@ -92,219 +92,219 @@ lu_2011_tax_VP-TR-3-n,Vente Prestations 3% - Triangulaire (-),VP-TR-3-n,sale,per lu_2011_tax_V-ART-43,Vente - Autres Exonérations (Article 43),V-ART-43,sale,percent,0.0000,0,502,,lu_2011_account_4614115,lu_2011_account_4614115,1,1,lu_tax_code_template_b_V-ART-43,lu_tax_code_template_t_V-ART-43,-1,-1,lu_tax_code_template_b_V-ART-43,lu_tax_code_template_t_V-ART-43,lu_2011_chart_1 lu_2011_tax_V-ART-44_58q,Vente - Autres Exonérations (Article 44 et 58 quater),V-ART-44_58q,sale,percent,0.0000,0,503,,lu_2011_account_4614115,lu_2011_account_4614115,1,1,lu_tax_code_template_b_V-ART-44_58q,lu_tax_code_template_t_V-ART-44_58q,-1,-1,lu_tax_code_template_b_V-ART-44_58q,lu_tax_code_template_t_V-ART-44_58q,lu_2011_chart_1 lu_2011_tax_VP-OPER,Vente Prestations - Opérations réalisées à l'étranger ,VP-OPER,sale,percent,0.0000,0,504,,lu_2011_account_4614115,lu_2011_account_4614115,1,1,lu_tax_code_template_b_VP-OPER,lu_tax_code_template_t_VP-OPER,-1,-1,lu_tax_code_template_b_VP-OPER,lu_tax_code_template_t_VP-OPER,lu_2011_chart_1 -lu_2011_tax_FB-PA-15,Frais Biens 15% - Pays,FB-PA-15,purchase,percent,0.1500,0,121,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FB-PA-15,lu_tax_code_template_t_FB-PA-15,-1,-1,lu_tax_code_template_b_FB-PA-15,lu_tax_code_template_t_FB-PA-15,lu_2011_chart_1 -lu_2011_tax_FB-PA-12,Frais Biens 12% - Pays,FB-PA-12,purchase,percent,0.1200,0,122,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FB-PA-12,lu_tax_code_template_t_FB-PA-12,-1,-1,lu_tax_code_template_b_FB-PA-12,lu_tax_code_template_t_FB-PA-12,lu_2011_chart_1 -lu_2011_tax_FB-PA-6,Frais Biens 6% - Pays,FB-PA-6,purchase,percent,0.0600,0,123,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FB-PA-6,lu_tax_code_template_t_FB-PA-6,-1,-1,lu_tax_code_template_b_FB-PA-6,lu_tax_code_template_t_FB-PA-6,lu_2011_chart_1 -lu_2011_tax_FB-PA-3,Frais Biens 3% - Pays,FB-PA-3,purchase,percent,0.0300,0,124,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FB-PA-3,lu_tax_code_template_t_FB-PA-3,-1,-1,lu_tax_code_template_b_FB-PA-3,lu_tax_code_template_t_FB-PA-3,lu_2011_chart_1 -lu_2011_tax_FB-PA-0,Frais Biens 0% - Pays,FB-PA-0,purchase,percent,0.0000,0,125,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FB-PA-0,lu_tax_code_template_t_FB-PA-0,-1,-1,lu_tax_code_template_b_FB-PA-0,lu_tax_code_template_t_FB-PA-0,lu_2011_chart_1 +lu_2011_tax_FB-PA-15,Frais Biens 15% - Pays,FB-PA-15,purchase,percent,0.1500,0,121,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FB-PA-15,lu_tax_code_template_t_FB-PA-15,-1,-1,lu_tax_code_template_b_FB-PA-15,lu_tax_code_template_t_FB-PA-15,lu_2011_chart_1 +lu_2011_tax_FB-PA-12,Frais Biens 12% - Pays,FB-PA-12,purchase,percent,0.1200,0,122,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FB-PA-12,lu_tax_code_template_t_FB-PA-12,-1,-1,lu_tax_code_template_b_FB-PA-12,lu_tax_code_template_t_FB-PA-12,lu_2011_chart_1 +lu_2011_tax_FB-PA-6,Frais Biens 6% - Pays,FB-PA-6,purchase,percent,0.0600,0,123,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FB-PA-6,lu_tax_code_template_t_FB-PA-6,-1,-1,lu_tax_code_template_b_FB-PA-6,lu_tax_code_template_t_FB-PA-6,lu_2011_chart_1 +lu_2011_tax_FB-PA-3,Frais Biens 3% - Pays,FB-PA-3,purchase,percent,0.0300,0,124,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FB-PA-3,lu_tax_code_template_t_FB-PA-3,-1,-1,lu_tax_code_template_b_FB-PA-3,lu_tax_code_template_t_FB-PA-3,lu_2011_chart_1 +lu_2011_tax_FB-PA-0,Frais Biens 0% - Pays,FB-PA-0,purchase,percent,0.0000,0,125,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FB-PA-0,lu_tax_code_template_t_FB-PA-0,-1,-1,lu_tax_code_template_b_FB-PA-0,lu_tax_code_template_t_FB-PA-0,lu_2011_chart_1 lu_2011_tax_FB-IC-15,Frais Biens 15% - Intracommunautaire,FB-IC-15,purchase,percent,1,1,221,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-IC-15-p,Frais Biens 15% - Intracommunautaire (+),FB-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FB-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FB-IC-15,lu_tax_code_template_t_FB-IC-15,-1,-1,lu_tax_code_template_b_FB-IC-15,lu_tax_code_template_t_FB-IC-15,lu_2011_chart_1 -lu_2011_tax_FB-IC-15-n,Frais Biens 15% - Intracommunautaire (-),FB-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FB-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-IC-15-p,Frais Biens 15% - Intracommunautaire (+),FB-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FB-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FB-IC-15,lu_tax_code_template_t_FB-IC-15,-1,-1,lu_tax_code_template_b_FB-IC-15,lu_tax_code_template_t_FB-IC-15,lu_2011_chart_1 +lu_2011_tax_FB-IC-15-n,Frais Biens 15% - Intracommunautaire (-),FB-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FB-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FB-IC-12,Frais Biens 12% - Intracommunautaire,FB-IC-12,purchase,percent,1,1,222,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-IC-12-p,Frais Biens 12% - Intracommunautaire (+),FB-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FB-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FB-IC-12,lu_tax_code_template_t_FB-IC-12,-1,-1,lu_tax_code_template_b_FB-IC-12,lu_tax_code_template_t_FB-IC-12,lu_2011_chart_1 -lu_2011_tax_FB-IC-12-n,Frais Biens 12% - Intracommunautaire (-),FB-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FB-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-IC-12-p,Frais Biens 12% - Intracommunautaire (+),FB-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FB-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FB-IC-12,lu_tax_code_template_t_FB-IC-12,-1,-1,lu_tax_code_template_b_FB-IC-12,lu_tax_code_template_t_FB-IC-12,lu_2011_chart_1 +lu_2011_tax_FB-IC-12-n,Frais Biens 12% - Intracommunautaire (-),FB-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FB-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FB-IC-6,Frais Biens 6% - Intracommunautaire,FB-IC-6,purchase,percent,1,1,223,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-IC-6-p,Frais Biens 6% - Intracommunautaire (+),FB-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FB-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FB-IC-6,lu_tax_code_template_t_FB-IC-6,-1,-1,lu_tax_code_template_b_FB-IC-6,lu_tax_code_template_t_FB-IC-6,lu_2011_chart_1 -lu_2011_tax_FB-IC-6-n,Frais Biens 6% - Intracommunautaire (-),FB-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FB-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-IC-6-p,Frais Biens 6% - Intracommunautaire (+),FB-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FB-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FB-IC-6,lu_tax_code_template_t_FB-IC-6,-1,-1,lu_tax_code_template_b_FB-IC-6,lu_tax_code_template_t_FB-IC-6,lu_2011_chart_1 +lu_2011_tax_FB-IC-6-n,Frais Biens 6% - Intracommunautaire (-),FB-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FB-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FB-IC-3,Frais Biens 3% - Intracommunautaire,FB-IC-3,purchase,percent,1,1,224,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-IC-3-p,Frais Biens 3% - Intracommunautaire (+),FB-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FB-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FB-IC-3,lu_tax_code_template_t_FB-IC-3,-1,-1,lu_tax_code_template_b_FB-IC-3,lu_tax_code_template_t_FB-IC-3,lu_2011_chart_1 -lu_2011_tax_FB-IC-3-n,Frais Biens 3% - Intracommunautaire (-),FB-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FB-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_FB-IC-0,Frais Biens 0% - Intracommunautaire,FB-IC-0,purchase,percent,0,0,225,,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FB-IC-0,lu_tax_code_template_t_FB-IC-0,-1,-1,lu_tax_code_template_b_FB-IC-0,lu_tax_code_template_t_FB-IC-0,lu_2011_chart_1 +lu_2011_tax_FB-IC-3-p,Frais Biens 3% - Intracommunautaire (+),FB-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FB-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FB-IC-3,lu_tax_code_template_t_FB-IC-3,-1,-1,lu_tax_code_template_b_FB-IC-3,lu_tax_code_template_t_FB-IC-3,lu_2011_chart_1 +lu_2011_tax_FB-IC-3-n,Frais Biens 3% - Intracommunautaire (-),FB-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FB-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-IC-0,Frais Biens 0% - Intracommunautaire,FB-IC-0,purchase,percent,0,0,225,,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FB-IC-0,lu_tax_code_template_t_FB-IC-0,-1,-1,lu_tax_code_template_b_FB-IC-0,lu_tax_code_template_t_FB-IC-0,lu_2011_chart_1 lu_2011_tax_FB-EC-15,Frais Biens 15% - Extracommunautaire,FB-EC-15,purchase,percent,1,1,321,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-EC-15-p,Frais Biens 15% - Extracommunautaire (+),FB-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FB-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FB-EC-15,lu_tax_code_template_t_FB-EC-15,-1,-1,lu_tax_code_template_b_FB-EC-15,lu_tax_code_template_t_FB-EC-15,lu_2011_chart_1 -lu_2011_tax_FB-EC-15-n,Frais Biens 15% - Extracommunautaire (-),FB-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FB-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-EC-15-p,Frais Biens 15% - Extracommunautaire (+),FB-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FB-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FB-EC-15,lu_tax_code_template_t_FB-EC-15,-1,-1,lu_tax_code_template_b_FB-EC-15,lu_tax_code_template_t_FB-EC-15,lu_2011_chart_1 +lu_2011_tax_FB-EC-15-n,Frais Biens 15% - Extracommunautaire (-),FB-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FB-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FB-EC-12,Frais Biens 12% - Extracommunautaire,FB-EC-12,purchase,percent,1,1,322,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-EC-12-p,Frais Biens 12% - Extracommunautaire (+),FB-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FB-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FB-EC-12,lu_tax_code_template_t_FB-EC-12,-1,-1,lu_tax_code_template_b_FB-EC-12,lu_tax_code_template_t_FB-EC-12,lu_2011_chart_1 -lu_2011_tax_FB-EC-12-n,Frais Biens 12% - Extracommunautaire (-),FB-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FB-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-EC-12-p,Frais Biens 12% - Extracommunautaire (+),FB-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FB-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FB-EC-12,lu_tax_code_template_t_FB-EC-12,-1,-1,lu_tax_code_template_b_FB-EC-12,lu_tax_code_template_t_FB-EC-12,lu_2011_chart_1 +lu_2011_tax_FB-EC-12-n,Frais Biens 12% - Extracommunautaire (-),FB-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FB-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FB-EC-6,Frais Biens 6% - Extracommunautaire,FB-EC-6,purchase,percent,1,1,323,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-EC-6-p,Frais Biens 6% - Extracommunautaire (+),FB-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FB-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FB-EC-6,lu_tax_code_template_t_FB-EC-6,-1,-1,lu_tax_code_template_b_FB-EC-6,lu_tax_code_template_t_FB-EC-6,lu_2011_chart_1 -lu_2011_tax_FB-EC-6-n,Frais Biens 6% - Extracommunautaire (-),FB-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FB-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-EC-6-p,Frais Biens 6% - Extracommunautaire (+),FB-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FB-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FB-EC-6,lu_tax_code_template_t_FB-EC-6,-1,-1,lu_tax_code_template_b_FB-EC-6,lu_tax_code_template_t_FB-EC-6,lu_2011_chart_1 +lu_2011_tax_FB-EC-6-n,Frais Biens 6% - Extracommunautaire (-),FB-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FB-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FB-EC-3,Frais Biens 3% - Extracommunautaire,FB-EC-3,purchase,percent,1,1,324,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FB-EC-3-p,Frais Biens 3% - Extracommunautaire (+),FB-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FB-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FB-EC-3,lu_tax_code_template_t_FB-EC-3,-1,-1,lu_tax_code_template_b_FB-EC-3,lu_tax_code_template_t_FB-EC-3,lu_2011_chart_1 -lu_2011_tax_FB-EC-3-n,Frais Biens 3% - Extracommunautaire (-),FB-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FB-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_FB-EC-0,Frais Biens 0% - Extracommunautaire,FB-EC-0,purchase,percent,0,0,325,,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FB-EC-0,lu_tax_code_template_t_FB-EC-0,-1,-1,lu_tax_code_template_b_FB-EC-0,lu_tax_code_template_t_FB-EC-0,lu_2011_chart_1 -lu_2011_tax_FP-PA-15,Frais Prestations 15% - Pays,FP-PA-15,purchase,percent,0.1500,0,131,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FP-PA-15,lu_tax_code_template_t_FP-PA-15,-1,-1,lu_tax_code_template_b_FP-PA-15,lu_tax_code_template_t_FP-PA-15,lu_2011_chart_1 -lu_2011_tax_FP-PA-12,Frais Prestations 12% - Pays,FP-PA-12,purchase,percent,0.1200,0,132,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FP-PA-12,lu_tax_code_template_t_FP-PA-12,-1,-1,lu_tax_code_template_b_FP-PA-12,lu_tax_code_template_t_FP-PA-12,lu_2011_chart_1 -lu_2011_tax_FP-PA-6,Frais Prestations 6% - Pays,FP-PA-6,purchase,percent,0.0600,0,133,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FP-PA-6,lu_tax_code_template_t_FP-PA-6,-1,-1,lu_tax_code_template_b_FP-PA-6,lu_tax_code_template_t_FP-PA-6,lu_2011_chart_1 -lu_2011_tax_FP-PA-3,Frais Prestations 3% - Pays,FP-PA-3,purchase,percent,0.0300,0,134,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FP-PA-3,lu_tax_code_template_t_FP-PA-3,-1,-1,lu_tax_code_template_b_FP-PA-3,lu_tax_code_template_t_FP-PA-3,lu_2011_chart_1 -lu_2011_tax_FP-PA-0,Frais Prestations 0% - Pays,FP-PA-0,purchase,percent,0.0000,0,135,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_FP-PA-0,lu_tax_code_template_t_FP-PA-0,-1,-1,lu_tax_code_template_b_FP-PA-0,lu_tax_code_template_t_FP-PA-0,lu_2011_chart_1 +lu_2011_tax_FB-EC-3-p,Frais Biens 3% - Extracommunautaire (+),FB-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FB-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FB-EC-3,lu_tax_code_template_t_FB-EC-3,-1,-1,lu_tax_code_template_b_FB-EC-3,lu_tax_code_template_t_FB-EC-3,lu_2011_chart_1 +lu_2011_tax_FB-EC-3-n,Frais Biens 3% - Extracommunautaire (-),FB-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FB-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FB-EC-0,Frais Biens 0% - Extracommunautaire,FB-EC-0,purchase,percent,0,0,325,,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FB-EC-0,lu_tax_code_template_t_FB-EC-0,-1,-1,lu_tax_code_template_b_FB-EC-0,lu_tax_code_template_t_FB-EC-0,lu_2011_chart_1 +lu_2011_tax_FP-PA-15,Frais Prestations 15% - Pays,FP-PA-15,purchase,percent,0.1500,0,131,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FP-PA-15,lu_tax_code_template_t_FP-PA-15,-1,-1,lu_tax_code_template_b_FP-PA-15,lu_tax_code_template_t_FP-PA-15,lu_2011_chart_1 +lu_2011_tax_FP-PA-12,Frais Prestations 12% - Pays,FP-PA-12,purchase,percent,0.1200,0,132,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FP-PA-12,lu_tax_code_template_t_FP-PA-12,-1,-1,lu_tax_code_template_b_FP-PA-12,lu_tax_code_template_t_FP-PA-12,lu_2011_chart_1 +lu_2011_tax_FP-PA-6,Frais Prestations 6% - Pays,FP-PA-6,purchase,percent,0.0600,0,133,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FP-PA-6,lu_tax_code_template_t_FP-PA-6,-1,-1,lu_tax_code_template_b_FP-PA-6,lu_tax_code_template_t_FP-PA-6,lu_2011_chart_1 +lu_2011_tax_FP-PA-3,Frais Prestations 3% - Pays,FP-PA-3,purchase,percent,0.0300,0,134,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FP-PA-3,lu_tax_code_template_t_FP-PA-3,-1,-1,lu_tax_code_template_b_FP-PA-3,lu_tax_code_template_t_FP-PA-3,lu_2011_chart_1 +lu_2011_tax_FP-PA-0,Frais Prestations 0% - Pays,FP-PA-0,purchase,percent,0.0000,0,135,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_FP-PA-0,lu_tax_code_template_t_FP-PA-0,-1,-1,lu_tax_code_template_b_FP-PA-0,lu_tax_code_template_t_FP-PA-0,lu_2011_chart_1 lu_2011_tax_FP-IC-15,Frais Prestations 15% - Intracommunautaire,FP-IC-15,purchase,percent,1,1,231,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-IC-15-p,Frais Prestations 15% - Intracommunautaire (+),FP-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FP-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FP-IC-15,lu_tax_code_template_t_FP-IC-15,-1,-1,lu_tax_code_template_b_FP-IC-15,lu_tax_code_template_t_FP-IC-15,lu_2011_chart_1 -lu_2011_tax_FP-IC-15-n,Frais Prestations 15% - Intracommunautaire (-),FP-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FP-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-IC-15-p,Frais Prestations 15% - Intracommunautaire (+),FP-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FP-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FP-IC-15,lu_tax_code_template_t_FP-IC-15,-1,-1,lu_tax_code_template_b_FP-IC-15,lu_tax_code_template_t_FP-IC-15,lu_2011_chart_1 +lu_2011_tax_FP-IC-15-n,Frais Prestations 15% - Intracommunautaire (-),FP-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FP-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FP-IC-12,Frais Prestations 12% - Intracommunautaire,FP-IC-12,purchase,percent,1,1,232,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-IC-12-p,Frais Prestations 12% - Intracommunautaire (+),FP-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FP-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FP-IC-12,lu_tax_code_template_t_FP-IC-12,-1,-1,lu_tax_code_template_b_FP-IC-12,lu_tax_code_template_t_FP-IC-12,lu_2011_chart_1 -lu_2011_tax_FP-IC-12-n,Frais Prestations 12% - Intracommunautaire (-),FP-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FP-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-IC-12-p,Frais Prestations 12% - Intracommunautaire (+),FP-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FP-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FP-IC-12,lu_tax_code_template_t_FP-IC-12,-1,-1,lu_tax_code_template_b_FP-IC-12,lu_tax_code_template_t_FP-IC-12,lu_2011_chart_1 +lu_2011_tax_FP-IC-12-n,Frais Prestations 12% - Intracommunautaire (-),FP-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FP-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FP-IC-6,Frais Prestations 6% - Intracommunautaire,FP-IC-6,purchase,percent,1,1,233,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-IC-6-p,Frais Prestations 6% - Intracommunautaire (+),FP-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FP-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FP-IC-6,lu_tax_code_template_t_FP-IC-6,-1,-1,lu_tax_code_template_b_FP-IC-6,lu_tax_code_template_t_FP-IC-6,lu_2011_chart_1 -lu_2011_tax_FP-IC-6-n,Frais Prestations 6% - Intracommunautaire (-),FP-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FP-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-IC-6-p,Frais Prestations 6% - Intracommunautaire (+),FP-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FP-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FP-IC-6,lu_tax_code_template_t_FP-IC-6,-1,-1,lu_tax_code_template_b_FP-IC-6,lu_tax_code_template_t_FP-IC-6,lu_2011_chart_1 +lu_2011_tax_FP-IC-6-n,Frais Prestations 6% - Intracommunautaire (-),FP-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FP-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FP-IC-3,Frais Prestations 3% - Intracommunautaire,FP-IC-3,purchase,percent,1,1,234,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-IC-3-p,Frais Prestations 3% - Intracommunautaire (+),FP-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FP-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FP-IC-3,lu_tax_code_template_t_FP-IC-3,-1,-1,lu_tax_code_template_b_FP-IC-3,lu_tax_code_template_t_FP-IC-3,lu_2011_chart_1 -lu_2011_tax_FP-IC-3-n,Frais Prestations 3% - Intracommunautaire (-),FP-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FP-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_FP-IC-0,Frais Prestations 0% - Intracommunautaire,FP-IC-0,purchase,percent,0,0,235,,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_FP-IC-0,lu_tax_code_template_t_FP-IC-0,-1,-1,lu_tax_code_template_b_FP-IC-0,lu_tax_code_template_t_FP-IC-0,lu_2011_chart_1 +lu_2011_tax_FP-IC-3-p,Frais Prestations 3% - Intracommunautaire (+),FP-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FP-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FP-IC-3,lu_tax_code_template_t_FP-IC-3,-1,-1,lu_tax_code_template_b_FP-IC-3,lu_tax_code_template_t_FP-IC-3,lu_2011_chart_1 +lu_2011_tax_FP-IC-3-n,Frais Prestations 3% - Intracommunautaire (-),FP-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FP-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-IC-0,Frais Prestations 0% - Intracommunautaire,FP-IC-0,purchase,percent,0,0,235,,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_FP-IC-0,lu_tax_code_template_t_FP-IC-0,-1,-1,lu_tax_code_template_b_FP-IC-0,lu_tax_code_template_t_FP-IC-0,lu_2011_chart_1 lu_2011_tax_FP-EC-15,Frais Prestations 15% - Extracommunautaire,FP-EC-15,purchase,percent,1,1,331,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-EC-15-p,Frais Prestations 15% - Extracommunautaire (+),FP-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FP-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FP-EC-15,lu_tax_code_template_t_FP-EC-15,-1,-1,lu_tax_code_template_b_FP-EC-15,lu_tax_code_template_t_FP-EC-15,lu_2011_chart_1 -lu_2011_tax_FP-EC-15-n,Frais Prestations 15% - Extracommunautaire (-),FP-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FP-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-EC-15-p,Frais Prestations 15% - Extracommunautaire (+),FP-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_FP-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FP-EC-15,lu_tax_code_template_t_FP-EC-15,-1,-1,lu_tax_code_template_b_FP-EC-15,lu_tax_code_template_t_FP-EC-15,lu_2011_chart_1 +lu_2011_tax_FP-EC-15-n,Frais Prestations 15% - Extracommunautaire (-),FP-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_FP-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FP-EC-12,Frais Prestations 12% - Extracommunautaire,FP-EC-12,purchase,percent,1,1,332,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-EC-12-p,Frais Prestations 12% - Extracommunautaire (+),FP-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FP-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FP-EC-12,lu_tax_code_template_t_FP-EC-12,-1,-1,lu_tax_code_template_b_FP-EC-12,lu_tax_code_template_t_FP-EC-12,lu_2011_chart_1 -lu_2011_tax_FP-EC-12-n,Frais Prestations 12% - Extracommunautaire (-),FP-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FP-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-EC-12-p,Frais Prestations 12% - Extracommunautaire (+),FP-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_FP-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FP-EC-12,lu_tax_code_template_t_FP-EC-12,-1,-1,lu_tax_code_template_b_FP-EC-12,lu_tax_code_template_t_FP-EC-12,lu_2011_chart_1 +lu_2011_tax_FP-EC-12-n,Frais Prestations 12% - Extracommunautaire (-),FP-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_FP-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FP-EC-6,Frais Prestations 6% - Extracommunautaire,FP-EC-6,purchase,percent,1,1,333,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-EC-6-p,Frais Prestations 6% - Extracommunautaire (+),FP-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FP-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FP-EC-6,lu_tax_code_template_t_FP-EC-6,-1,-1,lu_tax_code_template_b_FP-EC-6,lu_tax_code_template_t_FP-EC-6,lu_2011_chart_1 -lu_2011_tax_FP-EC-6-n,Frais Prestations 6% - Extracommunautaire (-),FP-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FP-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-EC-6-p,Frais Prestations 6% - Extracommunautaire (+),FP-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_FP-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FP-EC-6,lu_tax_code_template_t_FP-EC-6,-1,-1,lu_tax_code_template_b_FP-EC-6,lu_tax_code_template_t_FP-EC-6,lu_2011_chart_1 +lu_2011_tax_FP-EC-6-n,Frais Prestations 6% - Extracommunautaire (-),FP-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_FP-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_FP-EC-3,Frais Prestations 3% - Extracommunautaire,FP-EC-3,purchase,percent,1,1,334,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_FP-EC-3-p,Frais Prestations 3% - Extracommunautaire (+),FP-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FP-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FP-EC-3,lu_tax_code_template_t_FP-EC-3,-1,-1,lu_tax_code_template_b_FP-EC-3,lu_tax_code_template_t_FP-EC-3,lu_2011_chart_1 -lu_2011_tax_FP-EC-3-n,Frais Prestations 3% - Extracommunautaire (-),FP-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FP-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_FP-EC-0,Frais Prestations 0% - Extracommunautaire,FP-EC-0,purchase,percent,0,0,335,,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_FP-EC-0,lu_tax_code_template_t_FP-EC-0,-1,-1,lu_tax_code_template_b_FP-EC-0,lu_tax_code_template_t_FP-EC-0,lu_2011_chart_1 -lu_2011_tax_IB-PA-15,Investissement Biens 15% - Pays,IB-PA-15,purchase,percent,0.1500,0,141,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IB-PA-15,lu_tax_code_template_t_IB-PA-15,-1,-1,lu_tax_code_template_b_IB-PA-15,lu_tax_code_template_t_IB-PA-15,lu_2011_chart_1 -lu_2011_tax_IB-PA-12,Investissement Biens 12% - Pays,IB-PA-12,purchase,percent,0.1200,0,142,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IB-PA-12,lu_tax_code_template_t_IB-PA-12,-1,-1,lu_tax_code_template_b_IB-PA-12,lu_tax_code_template_t_IB-PA-12,lu_2011_chart_1 -lu_2011_tax_IB-PA-6,Investissement Biens 6% - Pays,IB-PA-6,purchase,percent,0.0600,0,143,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IB-PA-6,lu_tax_code_template_t_IB-PA-6,-1,-1,lu_tax_code_template_b_IB-PA-6,lu_tax_code_template_t_IB-PA-6,lu_2011_chart_1 -lu_2011_tax_IB-PA-3,Investissement Biens 3% - Pays,IB-PA-3,purchase,percent,0.0300,0,144,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IB-PA-3,lu_tax_code_template_t_IB-PA-3,-1,-1,lu_tax_code_template_b_IB-PA-3,lu_tax_code_template_t_IB-PA-3,lu_2011_chart_1 -lu_2011_tax_IB-PA-0,Investissement Biens 0% - Pays,IB-PA-0,purchase,percent,0.0000,0,145,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IB-PA-0,lu_tax_code_template_t_IB-PA-0,-1,-1,lu_tax_code_template_b_IB-PA-0,lu_tax_code_template_t_IB-PA-0,lu_2011_chart_1 +lu_2011_tax_FP-EC-3-p,Frais Prestations 3% - Extracommunautaire (+),FP-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_FP-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FP-EC-3,lu_tax_code_template_t_FP-EC-3,-1,-1,lu_tax_code_template_b_FP-EC-3,lu_tax_code_template_t_FP-EC-3,lu_2011_chart_1 +lu_2011_tax_FP-EC-3-n,Frais Prestations 3% - Extracommunautaire (-),FP-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_FP-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_FP-EC-0,Frais Prestations 0% - Extracommunautaire,FP-EC-0,purchase,percent,0,0,335,,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_FP-EC-0,lu_tax_code_template_t_FP-EC-0,-1,-1,lu_tax_code_template_b_FP-EC-0,lu_tax_code_template_t_FP-EC-0,lu_2011_chart_1 +lu_2011_tax_IB-PA-15,Investissement Biens 15% - Pays,IB-PA-15,purchase,percent,0.1500,0,141,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IB-PA-15,lu_tax_code_template_t_IB-PA-15,-1,-1,lu_tax_code_template_b_IB-PA-15,lu_tax_code_template_t_IB-PA-15,lu_2011_chart_1 +lu_2011_tax_IB-PA-12,Investissement Biens 12% - Pays,IB-PA-12,purchase,percent,0.1200,0,142,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IB-PA-12,lu_tax_code_template_t_IB-PA-12,-1,-1,lu_tax_code_template_b_IB-PA-12,lu_tax_code_template_t_IB-PA-12,lu_2011_chart_1 +lu_2011_tax_IB-PA-6,Investissement Biens 6% - Pays,IB-PA-6,purchase,percent,0.0600,0,143,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IB-PA-6,lu_tax_code_template_t_IB-PA-6,-1,-1,lu_tax_code_template_b_IB-PA-6,lu_tax_code_template_t_IB-PA-6,lu_2011_chart_1 +lu_2011_tax_IB-PA-3,Investissement Biens 3% - Pays,IB-PA-3,purchase,percent,0.0300,0,144,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IB-PA-3,lu_tax_code_template_t_IB-PA-3,-1,-1,lu_tax_code_template_b_IB-PA-3,lu_tax_code_template_t_IB-PA-3,lu_2011_chart_1 +lu_2011_tax_IB-PA-0,Investissement Biens 0% - Pays,IB-PA-0,purchase,percent,0.0000,0,145,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IB-PA-0,lu_tax_code_template_t_IB-PA-0,-1,-1,lu_tax_code_template_b_IB-PA-0,lu_tax_code_template_t_IB-PA-0,lu_2011_chart_1 lu_2011_tax_IB-IC-15,Investissement Biens 15% - Intracommunautaire,IB-IC-15,purchase,percent,1,1,241,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-IC-15-p,Investissement Biens 15% - Intracommunautaire (+),IB-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IB-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IB-IC-15,lu_tax_code_template_t_IB-IC-15,-1,-1,lu_tax_code_template_b_IB-IC-15,lu_tax_code_template_t_IB-IC-15,lu_2011_chart_1 -lu_2011_tax_IB-IC-15-n,Investissement Biens 15% - Intracommunautaire (-),IB-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IB-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-IC-15-p,Investissement Biens 15% - Intracommunautaire (+),IB-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IB-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IB-IC-15,lu_tax_code_template_t_IB-IC-15,-1,-1,lu_tax_code_template_b_IB-IC-15,lu_tax_code_template_t_IB-IC-15,lu_2011_chart_1 +lu_2011_tax_IB-IC-15-n,Investissement Biens 15% - Intracommunautaire (-),IB-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IB-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IB-IC-12,Investissement Biens 12% - Intracommunautaire,IB-IC-12,purchase,percent,1,1,242,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-IC-12-p,Investissement Biens 12% - Intracommunautaire (+),IB-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IB-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IB-IC-12,lu_tax_code_template_t_IB-IC-12,-1,-1,lu_tax_code_template_b_IB-IC-12,lu_tax_code_template_t_IB-IC-12,lu_2011_chart_1 -lu_2011_tax_IB-IC-12-n,Investissement Biens 12% - Intracommunautaire (-),IB-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IB-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-IC-12-p,Investissement Biens 12% - Intracommunautaire (+),IB-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IB-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IB-IC-12,lu_tax_code_template_t_IB-IC-12,-1,-1,lu_tax_code_template_b_IB-IC-12,lu_tax_code_template_t_IB-IC-12,lu_2011_chart_1 +lu_2011_tax_IB-IC-12-n,Investissement Biens 12% - Intracommunautaire (-),IB-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IB-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IB-IC-6,Investissement Biens 6% - Intracommunautaire,IB-IC-6,purchase,percent,1,1,243,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-IC-6-p,Investissement Biens 6% - Intracommunautaire (+),IB-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IB-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IB-IC-6,lu_tax_code_template_t_IB-IC-6,-1,-1,lu_tax_code_template_b_IB-IC-6,lu_tax_code_template_t_IB-IC-6,lu_2011_chart_1 -lu_2011_tax_IB-IC-6-n,Investissement Biens 6% - Intracommunautaire (-),IB-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IB-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-IC-6-p,Investissement Biens 6% - Intracommunautaire (+),IB-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IB-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IB-IC-6,lu_tax_code_template_t_IB-IC-6,-1,-1,lu_tax_code_template_b_IB-IC-6,lu_tax_code_template_t_IB-IC-6,lu_2011_chart_1 +lu_2011_tax_IB-IC-6-n,Investissement Biens 6% - Intracommunautaire (-),IB-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IB-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IB-IC-3,Investissement Biens 3% - Intracommunautaire,IB-IC-3,purchase,percent,1,1,244,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-IC-3-p,Investissement Biens 3% - Intracommunautaire (+),IB-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IB-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IB-IC-3,lu_tax_code_template_t_IB-IC-3,-1,-1,lu_tax_code_template_b_IB-IC-3,lu_tax_code_template_t_IB-IC-3,lu_2011_chart_1 -lu_2011_tax_IB-IC-3-n,Investissement Biens 3% - Intracommunautaire (-),IB-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IB-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_IB-IC-0,Investissement Biens 0% - Intracommunautaire,IB-IC-0,purchase,percent,0,0,245,,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IB-IC-0,lu_tax_code_template_t_IB-IC-0,-1,-1,lu_tax_code_template_b_IB-IC-0,lu_tax_code_template_t_IB-IC-0,lu_2011_chart_1 +lu_2011_tax_IB-IC-3-p,Investissement Biens 3% - Intracommunautaire (+),IB-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IB-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IB-IC-3,lu_tax_code_template_t_IB-IC-3,-1,-1,lu_tax_code_template_b_IB-IC-3,lu_tax_code_template_t_IB-IC-3,lu_2011_chart_1 +lu_2011_tax_IB-IC-3-n,Investissement Biens 3% - Intracommunautaire (-),IB-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IB-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-IC-0,Investissement Biens 0% - Intracommunautaire,IB-IC-0,purchase,percent,0,0,245,,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IB-IC-0,lu_tax_code_template_t_IB-IC-0,-1,-1,lu_tax_code_template_b_IB-IC-0,lu_tax_code_template_t_IB-IC-0,lu_2011_chart_1 lu_2011_tax_IB-EC-15,Investissement Biens 15% - Extracommunautaire,IB-EC-15,purchase,percent,1,1,341,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-EC-15-p,Investissement Biens 15% - Extracommunautaire (+),IB-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IB-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IB-EC-15,lu_tax_code_template_t_IB-EC-15,-1,-1,lu_tax_code_template_b_IB-EC-15,lu_tax_code_template_t_IB-EC-15,lu_2011_chart_1 -lu_2011_tax_IB-EC-15-n,Investissement Biens 15% - Extracommunautaire (-),IB-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IB-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-EC-15-p,Investissement Biens 15% - Extracommunautaire (+),IB-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IB-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IB-EC-15,lu_tax_code_template_t_IB-EC-15,-1,-1,lu_tax_code_template_b_IB-EC-15,lu_tax_code_template_t_IB-EC-15,lu_2011_chart_1 +lu_2011_tax_IB-EC-15-n,Investissement Biens 15% - Extracommunautaire (-),IB-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IB-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IB-EC-12,Investissement Biens 12% - Extracommunautaire,IB-EC-12,purchase,percent,1,1,342,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-EC-12-p,Investissement Biens 12% - Extracommunautaire (+),IB-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IB-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IB-EC-12,lu_tax_code_template_t_IB-EC-12,-1,-1,lu_tax_code_template_b_IB-EC-12,lu_tax_code_template_t_IB-EC-12,lu_2011_chart_1 -lu_2011_tax_IB-EC-12-n,Investissement Biens 12% - Extracommunautaire (-),IB-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IB-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-EC-12-p,Investissement Biens 12% - Extracommunautaire (+),IB-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IB-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IB-EC-12,lu_tax_code_template_t_IB-EC-12,-1,-1,lu_tax_code_template_b_IB-EC-12,lu_tax_code_template_t_IB-EC-12,lu_2011_chart_1 +lu_2011_tax_IB-EC-12-n,Investissement Biens 12% - Extracommunautaire (-),IB-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IB-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IB-EC-6,Investissement Biens 6% - Extracommunautaire,IB-EC-6,purchase,percent,1,1,343,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-EC-6-p,Investissement Biens 6% - Extracommunautaire (+),IB-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IB-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IB-EC-6,lu_tax_code_template_t_IB-EC-6,-1,-1,lu_tax_code_template_b_IB-EC-6,lu_tax_code_template_t_IB-EC-6,lu_2011_chart_1 -lu_2011_tax_IB-EC-6-n,Investissement Biens 6% - Extracommunautaire (-),IB-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IB-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-EC-6-p,Investissement Biens 6% - Extracommunautaire (+),IB-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IB-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IB-EC-6,lu_tax_code_template_t_IB-EC-6,-1,-1,lu_tax_code_template_b_IB-EC-6,lu_tax_code_template_t_IB-EC-6,lu_2011_chart_1 +lu_2011_tax_IB-EC-6-n,Investissement Biens 6% - Extracommunautaire (-),IB-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IB-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IB-EC-3,Investissement Biens 3% - Extracommunautaire,IB-EC-3,purchase,percent,1,1,344,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IB-EC-3-p,Investissement Biens 3% - Extracommunautaire (+),IB-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IB-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IB-EC-3,lu_tax_code_template_t_IB-EC-3,-1,-1,lu_tax_code_template_b_IB-EC-3,lu_tax_code_template_t_IB-EC-3,lu_2011_chart_1 -lu_2011_tax_IB-EC-3-n,Investissement Biens 3% - Extracommunautaire (-),IB-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IB-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_IB-EC-0,Investissement Biens 0% - Extracommunautaire,IB-EC-0,purchase,percent,0,0,345,,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IB-EC-0,lu_tax_code_template_t_IB-EC-0,-1,-1,lu_tax_code_template_b_IB-EC-0,lu_tax_code_template_t_IB-EC-0,lu_2011_chart_1 -lu_2011_tax_IP-PA-15,Investissement Prestations 15% - Pays,IP-PA-15,purchase,percent,0.1500,0,151,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IP-PA-15,lu_tax_code_template_t_IP-PA-15,-1,-1,lu_tax_code_template_b_IP-PA-15,lu_tax_code_template_t_IP-PA-15,lu_2011_chart_1 -lu_2011_tax_IP-PA-12,Investissement Prestations 12% - Pays,IP-PA-12,purchase,percent,0.1200,0,152,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IP-PA-12,lu_tax_code_template_t_IP-PA-12,-1,-1,lu_tax_code_template_b_IP-PA-12,lu_tax_code_template_t_IP-PA-12,lu_2011_chart_1 -lu_2011_tax_IP-PA-6,Investissement Prestations 6% - Pays,IP-PA-6,purchase,percent,0.0600,0,153,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IP-PA-6,lu_tax_code_template_t_IP-PA-6,-1,-1,lu_tax_code_template_b_IP-PA-6,lu_tax_code_template_t_IP-PA-6,lu_2011_chart_1 -lu_2011_tax_IP-PA-3,Investissement Prestations 3% - Pays,IP-PA-3,purchase,percent,0.0300,0,154,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IP-PA-3,lu_tax_code_template_t_IP-PA-3,-1,-1,lu_tax_code_template_b_IP-PA-3,lu_tax_code_template_t_IP-PA-3,lu_2011_chart_1 -lu_2011_tax_IP-PA-0,Investissement Prestations 0% - Pays,IP-PA-0,purchase,percent,0.0000,0,155,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_IP-PA-0,lu_tax_code_template_t_IP-PA-0,-1,-1,lu_tax_code_template_b_IP-PA-0,lu_tax_code_template_t_IP-PA-0,lu_2011_chart_1 +lu_2011_tax_IB-EC-3-p,Investissement Biens 3% - Extracommunautaire (+),IB-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IB-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IB-EC-3,lu_tax_code_template_t_IB-EC-3,-1,-1,lu_tax_code_template_b_IB-EC-3,lu_tax_code_template_t_IB-EC-3,lu_2011_chart_1 +lu_2011_tax_IB-EC-3-n,Investissement Biens 3% - Extracommunautaire (-),IB-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IB-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IB-EC-0,Investissement Biens 0% - Extracommunautaire,IB-EC-0,purchase,percent,0,0,345,,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IB-EC-0,lu_tax_code_template_t_IB-EC-0,-1,-1,lu_tax_code_template_b_IB-EC-0,lu_tax_code_template_t_IB-EC-0,lu_2011_chart_1 +lu_2011_tax_IP-PA-15,Investissement Prestations 15% - Pays,IP-PA-15,purchase,percent,0.1500,0,151,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IP-PA-15,lu_tax_code_template_t_IP-PA-15,-1,-1,lu_tax_code_template_b_IP-PA-15,lu_tax_code_template_t_IP-PA-15,lu_2011_chart_1 +lu_2011_tax_IP-PA-12,Investissement Prestations 12% - Pays,IP-PA-12,purchase,percent,0.1200,0,152,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IP-PA-12,lu_tax_code_template_t_IP-PA-12,-1,-1,lu_tax_code_template_b_IP-PA-12,lu_tax_code_template_t_IP-PA-12,lu_2011_chart_1 +lu_2011_tax_IP-PA-6,Investissement Prestations 6% - Pays,IP-PA-6,purchase,percent,0.0600,0,153,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IP-PA-6,lu_tax_code_template_t_IP-PA-6,-1,-1,lu_tax_code_template_b_IP-PA-6,lu_tax_code_template_t_IP-PA-6,lu_2011_chart_1 +lu_2011_tax_IP-PA-3,Investissement Prestations 3% - Pays,IP-PA-3,purchase,percent,0.0300,0,154,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IP-PA-3,lu_tax_code_template_t_IP-PA-3,-1,-1,lu_tax_code_template_b_IP-PA-3,lu_tax_code_template_t_IP-PA-3,lu_2011_chart_1 +lu_2011_tax_IP-PA-0,Investissement Prestations 0% - Pays,IP-PA-0,purchase,percent,0.0000,0,155,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_IP-PA-0,lu_tax_code_template_t_IP-PA-0,-1,-1,lu_tax_code_template_b_IP-PA-0,lu_tax_code_template_t_IP-PA-0,lu_2011_chart_1 lu_2011_tax_IP-IC-15,Investissement Prestations 15% - Intracommunautaire,IP-IC-15,purchase,percent,1,1,251,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-IC-15-p,Investissement Prestations 15% - Intracommunautaire (+),IP-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IP-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IP-IC-15,lu_tax_code_template_t_IP-IC-15,-1,-1,lu_tax_code_template_b_IP-IC-15,lu_tax_code_template_t_IP-IC-15,lu_2011_chart_1 -lu_2011_tax_IP-IC-15-n,Investissement Prestations 15% - Intracommunautaire (-),IP-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IP-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-IC-15-p,Investissement Prestations 15% - Intracommunautaire (+),IP-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IP-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IP-IC-15,lu_tax_code_template_t_IP-IC-15,-1,-1,lu_tax_code_template_b_IP-IC-15,lu_tax_code_template_t_IP-IC-15,lu_2011_chart_1 +lu_2011_tax_IP-IC-15-n,Investissement Prestations 15% - Intracommunautaire (-),IP-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IP-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IP-IC-12,Investissement Prestations 12% - Intracommunautaire,IP-IC-12,purchase,percent,1,1,252,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-IC-12-p,Investissement Prestations 12% - Intracommunautaire (+),IP-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IP-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IP-IC-12,lu_tax_code_template_t_IP-IC-12,-1,-1,lu_tax_code_template_b_IP-IC-12,lu_tax_code_template_t_IP-IC-12,lu_2011_chart_1 -lu_2011_tax_IP-IC-12-n,Investissement Prestations 12% - Intracommunautaire (-),IP-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IP-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-IC-12-p,Investissement Prestations 12% - Intracommunautaire (+),IP-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IP-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IP-IC-12,lu_tax_code_template_t_IP-IC-12,-1,-1,lu_tax_code_template_b_IP-IC-12,lu_tax_code_template_t_IP-IC-12,lu_2011_chart_1 +lu_2011_tax_IP-IC-12-n,Investissement Prestations 12% - Intracommunautaire (-),IP-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IP-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IP-IC-6,Investissement Prestations 6% - Intracommunautaire,IP-IC-6,purchase,percent,1,1,253,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-IC-6-p,Investissement Prestations 6% - Intracommunautaire (+),IP-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IP-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IP-IC-6,lu_tax_code_template_t_IP-IC-6,-1,-1,lu_tax_code_template_b_IP-IC-6,lu_tax_code_template_t_IP-IC-6,lu_2011_chart_1 -lu_2011_tax_IP-IC-6-n,Investissement Prestations 6% - Intracommunautaire (-),IP-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IP-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-IC-6-p,Investissement Prestations 6% - Intracommunautaire (+),IP-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IP-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IP-IC-6,lu_tax_code_template_t_IP-IC-6,-1,-1,lu_tax_code_template_b_IP-IC-6,lu_tax_code_template_t_IP-IC-6,lu_2011_chart_1 +lu_2011_tax_IP-IC-6-n,Investissement Prestations 6% - Intracommunautaire (-),IP-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IP-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IP-IC-3,Investissement Prestations 3% - Intracommunautaire,IP-IC-3,purchase,percent,1,1,254,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-IC-3-p,Investissement Prestations 3% - Intracommunautaire (+),IP-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IP-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IP-IC-3,lu_tax_code_template_t_IP-IC-3,-1,-1,lu_tax_code_template_b_IP-IC-3,lu_tax_code_template_t_IP-IC-3,lu_2011_chart_1 -lu_2011_tax_IP-IC-3-n,Investissement Prestations 3% - Intracommunautaire (-),IP-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IP-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_IP-IC-0,Investissement Prestations 0% - Intracommunautaire,IP-IC-0,purchase,percent,0,0,255,,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_IP-IC-0,lu_tax_code_template_t_IP-IC-0,-1,-1,lu_tax_code_template_b_IP-IC-0,lu_tax_code_template_t_IP-IC-0,lu_2011_chart_1 +lu_2011_tax_IP-IC-3-p,Investissement Prestations 3% - Intracommunautaire (+),IP-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IP-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IP-IC-3,lu_tax_code_template_t_IP-IC-3,-1,-1,lu_tax_code_template_b_IP-IC-3,lu_tax_code_template_t_IP-IC-3,lu_2011_chart_1 +lu_2011_tax_IP-IC-3-n,Investissement Prestations 3% - Intracommunautaire (-),IP-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IP-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-IC-0,Investissement Prestations 0% - Intracommunautaire,IP-IC-0,purchase,percent,0,0,255,,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_IP-IC-0,lu_tax_code_template_t_IP-IC-0,-1,-1,lu_tax_code_template_b_IP-IC-0,lu_tax_code_template_t_IP-IC-0,lu_2011_chart_1 lu_2011_tax_IP-EC-15,Investissement Prestations 15% - Extracommunautaire,IP-EC-15,purchase,percent,1,1,351,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-EC-15-p,Investissement Prestations 15% - Extracommunautaire (+),IP-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IP-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IP-EC-15,lu_tax_code_template_t_IP-EC-15,-1,-1,lu_tax_code_template_b_IP-EC-15,lu_tax_code_template_t_IP-EC-15,lu_2011_chart_1 -lu_2011_tax_IP-EC-15-n,Investissement Prestations 15% - Extracommunautaire (-),IP-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IP-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-EC-15-p,Investissement Prestations 15% - Extracommunautaire (+),IP-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_IP-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IP-EC-15,lu_tax_code_template_t_IP-EC-15,-1,-1,lu_tax_code_template_b_IP-EC-15,lu_tax_code_template_t_IP-EC-15,lu_2011_chart_1 +lu_2011_tax_IP-EC-15-n,Investissement Prestations 15% - Extracommunautaire (-),IP-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_IP-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IP-EC-12,Investissement Prestations 12% - Extracommunautaire,IP-EC-12,purchase,percent,1,1,352,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-EC-12-p,Investissement Prestations 12% - Extracommunautaire (+),IP-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IP-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IP-EC-12,lu_tax_code_template_t_IP-EC-12,-1,-1,lu_tax_code_template_b_IP-EC-12,lu_tax_code_template_t_IP-EC-12,lu_2011_chart_1 -lu_2011_tax_IP-EC-12-n,Investissement Prestations 12% - Extracommunautaire (-),IP-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IP-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-EC-12-p,Investissement Prestations 12% - Extracommunautaire (+),IP-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_IP-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IP-EC-12,lu_tax_code_template_t_IP-EC-12,-1,-1,lu_tax_code_template_b_IP-EC-12,lu_tax_code_template_t_IP-EC-12,lu_2011_chart_1 +lu_2011_tax_IP-EC-12-n,Investissement Prestations 12% - Extracommunautaire (-),IP-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_IP-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IP-EC-6,Investissement Prestations 6% - Extracommunautaire,IP-EC-6,purchase,percent,1,1,353,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-EC-6-p,Investissement Prestations 6% - Extracommunautaire (+),IP-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IP-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IP-EC-6,lu_tax_code_template_t_IP-EC-6,-1,-1,lu_tax_code_template_b_IP-EC-6,lu_tax_code_template_t_IP-EC-6,lu_2011_chart_1 -lu_2011_tax_IP-EC-6-n,Investissement Prestations 6% - Extracommunautaire (-),IP-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IP-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-EC-6-p,Investissement Prestations 6% - Extracommunautaire (+),IP-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_IP-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IP-EC-6,lu_tax_code_template_t_IP-EC-6,-1,-1,lu_tax_code_template_b_IP-EC-6,lu_tax_code_template_t_IP-EC-6,lu_2011_chart_1 +lu_2011_tax_IP-EC-6-n,Investissement Prestations 6% - Extracommunautaire (-),IP-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_IP-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_IP-EC-3,Investissement Prestations 3% - Extracommunautaire,IP-EC-3,purchase,percent,1,1,354,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_IP-EC-3-p,Investissement Prestations 3% - Extracommunautaire (+),IP-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IP-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IP-EC-3,lu_tax_code_template_t_IP-EC-3,-1,-1,lu_tax_code_template_b_IP-EC-3,lu_tax_code_template_t_IP-EC-3,lu_2011_chart_1 -lu_2011_tax_IP-EC-3-n,Investissement Prestations 3% - Extracommunautaire (-),IP-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IP-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_IP-EC-0,Investissement Prestations 0% - Extracommunautaire,IP-EC-0,purchase,percent,0,0,355,,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_IP-EC-0,lu_tax_code_template_t_IP-EC-0,-1,-1,lu_tax_code_template_b_IP-EC-0,lu_tax_code_template_t_IP-EC-0,lu_2011_chart_1 -lu_2011_tax_AB-PA-15,Achat Biens 15% - Pays,AB-PA-15,purchase,percent,0.1500,0,161,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AB-PA-15,lu_tax_code_template_t_AB-PA-15,-1,-1,lu_tax_code_template_b_AB-PA-15,lu_tax_code_template_t_AB-PA-15,lu_2011_chart_1 -lu_2011_tax_AB-PA-12,Achat Biens 12% - Pays,AB-PA-12,purchase,percent,0.1200,0,162,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AB-PA-12,lu_tax_code_template_t_AB-PA-12,-1,-1,lu_tax_code_template_b_AB-PA-12,lu_tax_code_template_t_AB-PA-12,lu_2011_chart_1 -lu_2011_tax_AB-PA-9,Achat Biens 9% - Pays,AB-PA-9,purchase,percent,0.0900,0,163,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AB-PA-9,lu_tax_code_template_t_AB-PA-9,-1,-1,lu_tax_code_template_b_AB-PA-9,lu_tax_code_template_t_AB-PA-9,lu_2011_chart_1 -lu_2011_tax_AB-PA-6,Achat Biens 6% - Pays,AB-PA-6,purchase,percent,0.0600,0,164,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AB-PA-6,lu_tax_code_template_t_AB-PA-6,-1,-1,lu_tax_code_template_b_AB-PA-6,lu_tax_code_template_t_AB-PA-6,lu_2011_chart_1 -lu_2011_tax_AB-PA-3,Achat Biens 3% - Pays,AB-PA-3,purchase,percent,0.0300,0,165,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AB-PA-3,lu_tax_code_template_t_AB-PA-3,-1,-1,lu_tax_code_template_b_AB-PA-3,lu_tax_code_template_t_AB-PA-3,lu_2011_chart_1 -lu_2011_tax_AB-PA-0,Achat Biens 0% - Pays,AB-PA-0,purchase,percent,0.0000,0,166,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AB-PA-0,lu_tax_code_template_t_AB-PA-0,-1,-1,lu_tax_code_template_b_AB-PA-0,lu_tax_code_template_t_AB-PA-0,lu_2011_chart_1 -lu_2011_tax_AB-PA-Tab,Achat Biens Tabacs - Pays,AB-PA-Tab,purchase,percent,0.0000,0,167,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AB-PA-Tab,lu_tax_code_template_t_AB-PA-Tab,-1,-1,lu_tax_code_template_b_AB-PA-Tab,lu_tax_code_template_t_AB-PA-Tab,lu_2011_chart_1 +lu_2011_tax_IP-EC-3-p,Investissement Prestations 3% - Extracommunautaire (+),IP-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_IP-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IP-EC-3,lu_tax_code_template_t_IP-EC-3,-1,-1,lu_tax_code_template_b_IP-EC-3,lu_tax_code_template_t_IP-EC-3,lu_2011_chart_1 +lu_2011_tax_IP-EC-3-n,Investissement Prestations 3% - Extracommunautaire (-),IP-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_IP-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_IP-EC-0,Investissement Prestations 0% - Extracommunautaire,IP-EC-0,purchase,percent,0,0,355,,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_IP-EC-0,lu_tax_code_template_t_IP-EC-0,-1,-1,lu_tax_code_template_b_IP-EC-0,lu_tax_code_template_t_IP-EC-0,lu_2011_chart_1 +lu_2011_tax_AB-PA-15,Achat Biens 15% - Pays,AB-PA-15,purchase,percent,0.1500,0,161,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AB-PA-15,lu_tax_code_template_t_AB-PA-15,-1,-1,lu_tax_code_template_b_AB-PA-15,lu_tax_code_template_t_AB-PA-15,lu_2011_chart_1 +lu_2011_tax_AB-PA-12,Achat Biens 12% - Pays,AB-PA-12,purchase,percent,0.1200,0,162,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AB-PA-12,lu_tax_code_template_t_AB-PA-12,-1,-1,lu_tax_code_template_b_AB-PA-12,lu_tax_code_template_t_AB-PA-12,lu_2011_chart_1 +lu_2011_tax_AB-PA-9,Achat Biens 9% - Pays,AB-PA-9,purchase,percent,0.0900,0,163,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AB-PA-9,lu_tax_code_template_t_AB-PA-9,-1,-1,lu_tax_code_template_b_AB-PA-9,lu_tax_code_template_t_AB-PA-9,lu_2011_chart_1 +lu_2011_tax_AB-PA-6,Achat Biens 6% - Pays,AB-PA-6,purchase,percent,0.0600,0,164,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AB-PA-6,lu_tax_code_template_t_AB-PA-6,-1,-1,lu_tax_code_template_b_AB-PA-6,lu_tax_code_template_t_AB-PA-6,lu_2011_chart_1 +lu_2011_tax_AB-PA-3,Achat Biens 3% - Pays,AB-PA-3,purchase,percent,0.0300,0,165,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AB-PA-3,lu_tax_code_template_t_AB-PA-3,-1,-1,lu_tax_code_template_b_AB-PA-3,lu_tax_code_template_t_AB-PA-3,lu_2011_chart_1 +lu_2011_tax_AB-PA-0,Achat Biens 0% - Pays,AB-PA-0,purchase,percent,0.0000,0,166,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AB-PA-0,lu_tax_code_template_t_AB-PA-0,-1,-1,lu_tax_code_template_b_AB-PA-0,lu_tax_code_template_t_AB-PA-0,lu_2011_chart_1 +lu_2011_tax_AB-PA-Tab,Achat Biens Tabacs - Pays,AB-PA-Tab,purchase,percent,0.0000,0,167,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AB-PA-Tab,lu_tax_code_template_t_AB-PA-Tab,-1,-1,lu_tax_code_template_b_AB-PA-Tab,lu_tax_code_template_t_AB-PA-Tab,lu_2011_chart_1 lu_2011_tax_AB-IC-15,Achat Biens 15% - Intracommunautaire,AB-IC-15,purchase,percent,1,1,261,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-IC-15-p,Achat Biens 15% - Intracommunautaire (+),AB-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AB-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AB-IC-15,lu_tax_code_template_t_AB-IC-15,-1,-1,lu_tax_code_template_b_AB-IC-15,lu_tax_code_template_t_AB-IC-15,lu_2011_chart_1 -lu_2011_tax_AB-IC-15-n,Achat Biens 15% - Intracommunautaire (-),AB-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AB-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-IC-15-p,Achat Biens 15% - Intracommunautaire (+),AB-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AB-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AB-IC-15,lu_tax_code_template_t_AB-IC-15,-1,-1,lu_tax_code_template_b_AB-IC-15,lu_tax_code_template_t_AB-IC-15,lu_2011_chart_1 +lu_2011_tax_AB-IC-15-n,Achat Biens 15% - Intracommunautaire (-),AB-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AB-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-IC-12,Achat Biens 12% - Intracommunautaire,AB-IC-12,purchase,percent,1,1,262,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-IC-12-p,Achat Biens 12% - Intracommunautaire (+),AB-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AB-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AB-IC-12,lu_tax_code_template_t_AB-IC-12,-1,-1,lu_tax_code_template_b_AB-IC-12,lu_tax_code_template_t_AB-IC-12,lu_2011_chart_1 -lu_2011_tax_AB-IC-12-n,Achat Biens 12% - Intracommunautaire (-),AB-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AB-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-IC-12-p,Achat Biens 12% - Intracommunautaire (+),AB-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AB-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AB-IC-12,lu_tax_code_template_t_AB-IC-12,-1,-1,lu_tax_code_template_b_AB-IC-12,lu_tax_code_template_t_AB-IC-12,lu_2011_chart_1 +lu_2011_tax_AB-IC-12-n,Achat Biens 12% - Intracommunautaire (-),AB-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AB-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-IC-9,Achat Biens 9% - Intracommunautaire,AB-IC-9,purchase,percent,1,1,263,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-IC-9-p,Achat Biens 9% - Intracommunautaire (+),AB-IC-9-p,purchase,percent,0.0900,0,1000,lu_2011_tax_AB-IC-9,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AB-IC-9,lu_tax_code_template_t_AB-IC-9,-1,-1,lu_tax_code_template_b_AB-IC-9,lu_tax_code_template_t_AB-IC-9,lu_2011_chart_1 -lu_2011_tax_AB-IC-9-n,Achat Biens 9% - Intracommunautaire (-),AB-IC-9-n,purchase,percent,-0.0900,0,2000,lu_2011_tax_AB-IC-9,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-IC-9-p,Achat Biens 9% - Intracommunautaire (+),AB-IC-9-p,purchase,percent,0.0900,0,1000,lu_2011_tax_AB-IC-9,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AB-IC-9,lu_tax_code_template_t_AB-IC-9,-1,-1,lu_tax_code_template_b_AB-IC-9,lu_tax_code_template_t_AB-IC-9,lu_2011_chart_1 +lu_2011_tax_AB-IC-9-n,Achat Biens 9% - Intracommunautaire (-),AB-IC-9-n,purchase,percent,-0.0900,0,2000,lu_2011_tax_AB-IC-9,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-IC-6,Achat Biens 6% - Intracommunautaire,AB-IC-6,purchase,percent,1,1,264,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-IC-6-p,Achat Biens 6% - Intracommunautaire (+),AB-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AB-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AB-IC-6,lu_tax_code_template_t_AB-IC-6,-1,-1,lu_tax_code_template_b_AB-IC-6,lu_tax_code_template_t_AB-IC-6,lu_2011_chart_1 -lu_2011_tax_AB-IC-6-n,Achat Biens 6% - Intracommunautaire (-),AB-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AB-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-IC-6-p,Achat Biens 6% - Intracommunautaire (+),AB-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AB-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AB-IC-6,lu_tax_code_template_t_AB-IC-6,-1,-1,lu_tax_code_template_b_AB-IC-6,lu_tax_code_template_t_AB-IC-6,lu_2011_chart_1 +lu_2011_tax_AB-IC-6-n,Achat Biens 6% - Intracommunautaire (-),AB-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AB-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-IC-3,Achat Biens 3% - Intracommunautaire,AB-IC-3,purchase,percent,1,1,265,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-IC-3-p,Achat Biens 3% - Intracommunautaire (+),AB-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AB-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AB-IC-3,lu_tax_code_template_t_AB-IC-3,-1,-1,lu_tax_code_template_b_AB-IC-3,lu_tax_code_template_t_AB-IC-3,lu_2011_chart_1 -lu_2011_tax_AB-IC-3-n,Achat Biens 3% - Intracommunautaire (-),AB-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AB-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_AB-IC-0,Achat Biens 0% - Intracommunautaire,AB-IC-0,purchase,percent,0,0,266,,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AB-IC-0,lu_tax_code_template_t_AB-IC-0,-1,-1,lu_tax_code_template_b_AB-IC-0,lu_tax_code_template_t_AB-IC-0,lu_2011_chart_1 +lu_2011_tax_AB-IC-3-p,Achat Biens 3% - Intracommunautaire (+),AB-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AB-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AB-IC-3,lu_tax_code_template_t_AB-IC-3,-1,-1,lu_tax_code_template_b_AB-IC-3,lu_tax_code_template_t_AB-IC-3,lu_2011_chart_1 +lu_2011_tax_AB-IC-3-n,Achat Biens 3% - Intracommunautaire (-),AB-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AB-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-IC-0,Achat Biens 0% - Intracommunautaire,AB-IC-0,purchase,percent,0,0,266,,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AB-IC-0,lu_tax_code_template_t_AB-IC-0,-1,-1,lu_tax_code_template_b_AB-IC-0,lu_tax_code_template_t_AB-IC-0,lu_2011_chart_1 lu_2011_tax_AB-EC-15,Achat Biens 15% - Extracommunautaire,AB-EC-15,purchase,percent,1,1,361,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-EC-15-p,Achat Biens 15% - Extracommunautaire (+),AB-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AB-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AB-EC-15,lu_tax_code_template_t_AB-EC-15,-1,-1,lu_tax_code_template_b_AB-EC-15,lu_tax_code_template_t_AB-EC-15,lu_2011_chart_1 -lu_2011_tax_AB-EC-15-n,Achat Biens 15% - Extracommunautaire (-),AB-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AB-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-EC-15-p,Achat Biens 15% - Extracommunautaire (+),AB-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AB-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AB-EC-15,lu_tax_code_template_t_AB-EC-15,-1,-1,lu_tax_code_template_b_AB-EC-15,lu_tax_code_template_t_AB-EC-15,lu_2011_chart_1 +lu_2011_tax_AB-EC-15-n,Achat Biens 15% - Extracommunautaire (-),AB-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AB-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-EC-12,Achat Biens 12% - Extracommunautaire,AB-EC-12,purchase,percent,1,1,362,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-EC-12-p,Achat Biens 12% - Extracommunautaire (+),AB-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AB-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AB-EC-12,lu_tax_code_template_t_AB-EC-12,-1,-1,lu_tax_code_template_b_AB-EC-12,lu_tax_code_template_t_AB-EC-12,lu_2011_chart_1 -lu_2011_tax_AB-EC-12-n,Achat Biens 12% - Extracommunautaire (-),AB-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AB-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-EC-12-p,Achat Biens 12% - Extracommunautaire (+),AB-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AB-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AB-EC-12,lu_tax_code_template_t_AB-EC-12,-1,-1,lu_tax_code_template_b_AB-EC-12,lu_tax_code_template_t_AB-EC-12,lu_2011_chart_1 +lu_2011_tax_AB-EC-12-n,Achat Biens 12% - Extracommunautaire (-),AB-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AB-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-EC-6,Achat Biens 6% - Extracommunautaire,AB-EC-6,purchase,percent,1,1,363,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-EC-6-p,Achat Biens 6% - Extracommunautaire (+),AB-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AB-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AB-EC-6,lu_tax_code_template_t_AB-EC-6,-1,-1,lu_tax_code_template_b_AB-EC-6,lu_tax_code_template_t_AB-EC-6,lu_2011_chart_1 -lu_2011_tax_AB-EC-6-n,Achat Biens 6% - Extracommunautaire (-),AB-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AB-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-EC-6-p,Achat Biens 6% - Extracommunautaire (+),AB-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AB-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AB-EC-6,lu_tax_code_template_t_AB-EC-6,-1,-1,lu_tax_code_template_b_AB-EC-6,lu_tax_code_template_t_AB-EC-6,lu_2011_chart_1 +lu_2011_tax_AB-EC-6-n,Achat Biens 6% - Extracommunautaire (-),AB-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AB-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-EC-3,Achat Biens 3% - Extracommunautaire,AB-EC-3,purchase,percent,1,1,364,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-EC-3-p,Achat Biens 3% - Extracommunautaire (+),AB-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AB-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AB-EC-3,lu_tax_code_template_t_AB-EC-3,-1,-1,lu_tax_code_template_b_AB-EC-3,lu_tax_code_template_t_AB-EC-3,lu_2011_chart_1 -lu_2011_tax_AB-EC-3-n,Achat Biens 3% - Extracommunautaire (-),AB-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AB-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_AB-EC-0,Achat Biens 0% - Extracommunautaire,AB-EC-0,purchase,percent,0,0,365,,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AB-EC-0,lu_tax_code_template_t_AB-EC-0,-1,-1,lu_tax_code_template_b_AB-EC-0,lu_tax_code_template_t_AB-EC-0,lu_2011_chart_1 +lu_2011_tax_AB-EC-3-p,Achat Biens 3% - Extracommunautaire (+),AB-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AB-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AB-EC-3,lu_tax_code_template_t_AB-EC-3,-1,-1,lu_tax_code_template_b_AB-EC-3,lu_tax_code_template_t_AB-EC-3,lu_2011_chart_1 +lu_2011_tax_AB-EC-3-n,Achat Biens 3% - Extracommunautaire (-),AB-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AB-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-EC-0,Achat Biens 0% - Extracommunautaire,AB-EC-0,purchase,percent,0,0,365,,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AB-EC-0,lu_tax_code_template_t_AB-EC-0,-1,-1,lu_tax_code_template_b_AB-EC-0,lu_tax_code_template_t_AB-EC-0,lu_2011_chart_1 lu_2011_tax_AB-TR-15,Achat Biens 15% - Triangulaire,AB-TR-15,purchase,percent,1,1,461,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-TR-15-p,Achat Biens 15% - Triangulaire (+),AB-TR-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AB-TR-15,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AB-TR-15,lu_tax_code_template_t_AB-TR-15,-1,-1,lu_tax_code_template_b_AB-TR-15,lu_tax_code_template_t_AB-TR-15,lu_2011_chart_1 -lu_2011_tax_AB-TR-15-n,Achat Biens 15% - Triangulaire (-),AB-TR-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AB-TR-15,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-TR-15-p,Achat Biens 15% - Triangulaire (+),AB-TR-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AB-TR-15,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AB-TR-15,lu_tax_code_template_t_AB-TR-15,-1,-1,lu_tax_code_template_b_AB-TR-15,lu_tax_code_template_t_AB-TR-15,lu_2011_chart_1 +lu_2011_tax_AB-TR-15-n,Achat Biens 15% - Triangulaire (-),AB-TR-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AB-TR-15,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-TR-12,Achat Biens 12% - Triangulaire,AB-TR-12,purchase,percent,1,1,462,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-TR-12-p,Achat Biens 12% - Triangulaire (+),AB-TR-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AB-TR-12,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AB-TR-12,lu_tax_code_template_t_AB-TR-12,-1,-1,lu_tax_code_template_b_AB-TR-12,lu_tax_code_template_t_AB-TR-12,lu_2011_chart_1 -lu_2011_tax_AB-TR-12-n,Achat Biens 12% - Triangulaire (-),AB-TR-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AB-TR-12,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-TR-12-p,Achat Biens 12% - Triangulaire (+),AB-TR-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AB-TR-12,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AB-TR-12,lu_tax_code_template_t_AB-TR-12,-1,-1,lu_tax_code_template_b_AB-TR-12,lu_tax_code_template_t_AB-TR-12,lu_2011_chart_1 +lu_2011_tax_AB-TR-12-n,Achat Biens 12% - Triangulaire (-),AB-TR-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AB-TR-12,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-TR-6,Achat Biens 6% - Triangulaire,AB-TR-6,purchase,percent,1,1,463,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-TR-6-p,Achat Biens 6% - Triangulaire (+),AB-TR-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AB-TR-6,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AB-TR-6,lu_tax_code_template_t_AB-TR-6,-1,-1,lu_tax_code_template_b_AB-TR-6,lu_tax_code_template_t_AB-TR-6,lu_2011_chart_1 -lu_2011_tax_AB-TR-6-n,Achat Biens 6% - Triangulaire (-),AB-TR-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AB-TR-6,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AB-TR-6-p,Achat Biens 6% - Triangulaire (+),AB-TR-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AB-TR-6,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AB-TR-6,lu_tax_code_template_t_AB-TR-6,-1,-1,lu_tax_code_template_b_AB-TR-6,lu_tax_code_template_t_AB-TR-6,lu_2011_chart_1 +lu_2011_tax_AB-TR-6-n,Achat Biens 6% - Triangulaire (-),AB-TR-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AB-TR-6,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AB-TR-3,Achat Biens 3% - Triangulaire,AB-TR-3,purchase,percent,1,1,464,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AB-TR-3-p,Achat Biens 3% - Triangulaire (+),AB-TR-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AB-TR-3,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AB-TR-3,lu_tax_code_template_t_AB-TR-3,-1,-1,lu_tax_code_template_b_AB-TR-3,lu_tax_code_template_t_AB-TR-3,lu_2011_chart_1 -lu_2011_tax_AB-TR-3-n,Achat Biens 3% - Triangulaire (-),AB-TR-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AB-TR-3,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_AP-PA-15,Achat Prestations 15% - Pays,AP-PA-15,purchase,percent,0.1500,0,171,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AP-PA-15,lu_tax_code_template_t_AP-PA-15,-1,-1,lu_tax_code_template_b_AP-PA-15,lu_tax_code_template_t_AP-PA-15,lu_2011_chart_1 -lu_2011_tax_AP-PA-12,Achat Prestations 12% - Pays,AP-PA-12,purchase,percent,0.1200,0,172,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AP-PA-12,lu_tax_code_template_t_AP-PA-12,-1,-1,lu_tax_code_template_b_AP-PA-12,lu_tax_code_template_t_AP-PA-12,lu_2011_chart_1 -lu_2011_tax_AP-PA-9,Achat Prestations 9% - Pays,AP-PA-9,purchase,percent,0.0900,0,173,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AP-PA-9,lu_tax_code_template_t_AP-PA-9,-1,-1,lu_tax_code_template_b_AP-PA-9,lu_tax_code_template_t_AP-PA-9,lu_2011_chart_1 -lu_2011_tax_AP-PA-6,Achat Prestations 6% - Pays,AP-PA-6,purchase,percent,0.0600,0,174,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AP-PA-6,lu_tax_code_template_t_AP-PA-6,-1,-1,lu_tax_code_template_b_AP-PA-6,lu_tax_code_template_t_AP-PA-6,lu_2011_chart_1 -lu_2011_tax_AP-PA-3,Achat Prestations 3% - Pays,AP-PA-3,purchase,percent,0.0300,0,175,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AP-PA-3,lu_tax_code_template_t_AP-PA-3,-1,-1,lu_tax_code_template_b_AP-PA-3,lu_tax_code_template_t_AP-PA-3,lu_2011_chart_1 -lu_2011_tax_AP-PA-0,Achat Prestations 0% - Pays,AP-PA-0,purchase,percent,0.0000,0,176,,lu_2011_account_4226111,lu_2011_account_4226111,1,1,lu_tax_code_template_b_AP-PA-0,lu_tax_code_template_t_AP-PA-0,-1,-1,lu_tax_code_template_b_AP-PA-0,lu_tax_code_template_t_AP-PA-0,lu_2011_chart_1 +lu_2011_tax_AB-TR-3-p,Achat Biens 3% - Triangulaire (+),AB-TR-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AB-TR-3,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AB-TR-3,lu_tax_code_template_t_AB-TR-3,-1,-1,lu_tax_code_template_b_AB-TR-3,lu_tax_code_template_t_AB-TR-3,lu_2011_chart_1 +lu_2011_tax_AB-TR-3-n,Achat Biens 3% - Triangulaire (-),AB-TR-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AB-TR-3,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-PA-15,Achat Prestations 15% - Pays,AP-PA-15,purchase,percent,0.1500,0,171,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AP-PA-15,lu_tax_code_template_t_AP-PA-15,-1,-1,lu_tax_code_template_b_AP-PA-15,lu_tax_code_template_t_AP-PA-15,lu_2011_chart_1 +lu_2011_tax_AP-PA-12,Achat Prestations 12% - Pays,AP-PA-12,purchase,percent,0.1200,0,172,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AP-PA-12,lu_tax_code_template_t_AP-PA-12,-1,-1,lu_tax_code_template_b_AP-PA-12,lu_tax_code_template_t_AP-PA-12,lu_2011_chart_1 +lu_2011_tax_AP-PA-9,Achat Prestations 9% - Pays,AP-PA-9,purchase,percent,0.0900,0,173,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AP-PA-9,lu_tax_code_template_t_AP-PA-9,-1,-1,lu_tax_code_template_b_AP-PA-9,lu_tax_code_template_t_AP-PA-9,lu_2011_chart_1 +lu_2011_tax_AP-PA-6,Achat Prestations 6% - Pays,AP-PA-6,purchase,percent,0.0600,0,174,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AP-PA-6,lu_tax_code_template_t_AP-PA-6,-1,-1,lu_tax_code_template_b_AP-PA-6,lu_tax_code_template_t_AP-PA-6,lu_2011_chart_1 +lu_2011_tax_AP-PA-3,Achat Prestations 3% - Pays,AP-PA-3,purchase,percent,0.0300,0,175,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AP-PA-3,lu_tax_code_template_t_AP-PA-3,-1,-1,lu_tax_code_template_b_AP-PA-3,lu_tax_code_template_t_AP-PA-3,lu_2011_chart_1 +lu_2011_tax_AP-PA-0,Achat Prestations 0% - Pays,AP-PA-0,purchase,percent,0.0000,0,176,,lu_2011_account_4614111,lu_2011_account_4614111,1,1,lu_tax_code_template_b_AP-PA-0,lu_tax_code_template_t_AP-PA-0,-1,-1,lu_tax_code_template_b_AP-PA-0,lu_tax_code_template_t_AP-PA-0,lu_2011_chart_1 lu_2011_tax_AP-IC-15,Achat Prestations 15% - Intracommunautaire,AP-IC-15,purchase,percent,1,1,271,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-IC-15-p,Achat Prestations 15% - Intracommunautaire (+),AP-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AP-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AP-IC-15,lu_tax_code_template_t_AP-IC-15,-1,-1,lu_tax_code_template_b_AP-IC-15,lu_tax_code_template_t_AP-IC-15,lu_2011_chart_1 -lu_2011_tax_AP-IC-15-n,Achat Prestations 15% - Intracommunautaire (-),AP-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AP-IC-15,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-IC-15-p,Achat Prestations 15% - Intracommunautaire (+),AP-IC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AP-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AP-IC-15,lu_tax_code_template_t_AP-IC-15,-1,-1,lu_tax_code_template_b_AP-IC-15,lu_tax_code_template_t_AP-IC-15,lu_2011_chart_1 +lu_2011_tax_AP-IC-15-n,Achat Prestations 15% - Intracommunautaire (-),AP-IC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AP-IC-15,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-IC-12,Achat Prestations 12% - Intracommunautaire,AP-IC-12,purchase,percent,1,1,272,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-IC-12-p,Achat Prestations 12% - Intracommunautaire (+),AP-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AP-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AP-IC-12,lu_tax_code_template_t_AP-IC-12,-1,-1,lu_tax_code_template_b_AP-IC-12,lu_tax_code_template_t_AP-IC-12,lu_2011_chart_1 -lu_2011_tax_AP-IC-12-n,Achat Prestations 12% - Intracommunautaire (-),AP-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AP-IC-12,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-IC-12-p,Achat Prestations 12% - Intracommunautaire (+),AP-IC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AP-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AP-IC-12,lu_tax_code_template_t_AP-IC-12,-1,-1,lu_tax_code_template_b_AP-IC-12,lu_tax_code_template_t_AP-IC-12,lu_2011_chart_1 +lu_2011_tax_AP-IC-12-n,Achat Prestations 12% - Intracommunautaire (-),AP-IC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AP-IC-12,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-IC-6,Achat Prestations 6% - Intracommunautaire,AP-IC-6,purchase,percent,1,1,273,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-IC-6-p,Achat Prestations 6% - Intracommunautaire (+),AP-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AP-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AP-IC-6,lu_tax_code_template_t_AP-IC-6,-1,-1,lu_tax_code_template_b_AP-IC-6,lu_tax_code_template_t_AP-IC-6,lu_2011_chart_1 -lu_2011_tax_AP-IC-6-n,Achat Prestations 6% - Intracommunautaire (-),AP-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AP-IC-6,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-IC-6-p,Achat Prestations 6% - Intracommunautaire (+),AP-IC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AP-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AP-IC-6,lu_tax_code_template_t_AP-IC-6,-1,-1,lu_tax_code_template_b_AP-IC-6,lu_tax_code_template_t_AP-IC-6,lu_2011_chart_1 +lu_2011_tax_AP-IC-6-n,Achat Prestations 6% - Intracommunautaire (-),AP-IC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AP-IC-6,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-IC-3,Achat Prestations 3% - Intracommunautaire,AP-IC-3,purchase,percent,1,1,274,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-IC-3-p,Achat Prestations 3% - Intracommunautaire (+),AP-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AP-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AP-IC-3,lu_tax_code_template_t_AP-IC-3,-1,-1,lu_tax_code_template_b_AP-IC-3,lu_tax_code_template_t_AP-IC-3,lu_2011_chart_1 -lu_2011_tax_AP-IC-3-n,Achat Prestations 3% - Intracommunautaire (-),AP-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AP-IC-3,lu_2011_account_4226112,lu_2011_account_4226112,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_AP-IC-0,Achat Prestations 0% - Intracommunautaire,AP-IC-0,purchase,percent,0,0,275,,lu_2011_account_4226112,lu_2011_account_4226112,1,1,lu_tax_code_template_b_AP-IC-0,lu_tax_code_template_t_AP-IC-0,-1,-1,lu_tax_code_template_b_AP-IC-0,lu_tax_code_template_t_AP-IC-0,lu_2011_chart_1 +lu_2011_tax_AP-IC-3-p,Achat Prestations 3% - Intracommunautaire (+),AP-IC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AP-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AP-IC-3,lu_tax_code_template_t_AP-IC-3,-1,-1,lu_tax_code_template_b_AP-IC-3,lu_tax_code_template_t_AP-IC-3,lu_2011_chart_1 +lu_2011_tax_AP-IC-3-n,Achat Prestations 3% - Intracommunautaire (-),AP-IC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AP-IC-3,lu_2011_account_4614112,lu_2011_account_4614112,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-IC-0,Achat Prestations 0% - Intracommunautaire,AP-IC-0,purchase,percent,0,0,275,,lu_2011_account_4614112,lu_2011_account_4614112,1,1,lu_tax_code_template_b_AP-IC-0,lu_tax_code_template_t_AP-IC-0,-1,-1,lu_tax_code_template_b_AP-IC-0,lu_tax_code_template_t_AP-IC-0,lu_2011_chart_1 lu_2011_tax_AP-EC-15,Achat Prestations 15% - Extracommunautaire,AP-EC-15,purchase,percent,1,1,371,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-EC-15-p,Achat Prestations 15% - Extracommunautaire (+),AP-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AP-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AP-EC-15,lu_tax_code_template_t_AP-EC-15,-1,-1,lu_tax_code_template_b_AP-EC-15,lu_tax_code_template_t_AP-EC-15,lu_2011_chart_1 -lu_2011_tax_AP-EC-15-n,Achat Prestations 15% - Extracommunautaire (-),AP-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AP-EC-15,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-EC-15-p,Achat Prestations 15% - Extracommunautaire (+),AP-EC-15-p,purchase,percent,0.1500,0,1000,lu_2011_tax_AP-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AP-EC-15,lu_tax_code_template_t_AP-EC-15,-1,-1,lu_tax_code_template_b_AP-EC-15,lu_tax_code_template_t_AP-EC-15,lu_2011_chart_1 +lu_2011_tax_AP-EC-15-n,Achat Prestations 15% - Extracommunautaire (-),AP-EC-15-n,purchase,percent,-0.1500,0,2000,lu_2011_tax_AP-EC-15,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-EC-12,Achat Prestations 12% - Extracommunautaire,AP-EC-12,purchase,percent,1,1,372,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-EC-12-p,Achat Prestations 12% - Extracommunautaire (+),AP-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AP-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AP-EC-12,lu_tax_code_template_t_AP-EC-12,-1,-1,lu_tax_code_template_b_AP-EC-12,lu_tax_code_template_t_AP-EC-12,lu_2011_chart_1 -lu_2011_tax_AP-EC-12-n,Achat Prestations 12% - Extracommunautaire (-),AP-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AP-EC-12,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-EC-12-p,Achat Prestations 12% - Extracommunautaire (+),AP-EC-12-p,purchase,percent,0.1200,0,1000,lu_2011_tax_AP-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AP-EC-12,lu_tax_code_template_t_AP-EC-12,-1,-1,lu_tax_code_template_b_AP-EC-12,lu_tax_code_template_t_AP-EC-12,lu_2011_chart_1 +lu_2011_tax_AP-EC-12-n,Achat Prestations 12% - Extracommunautaire (-),AP-EC-12-n,purchase,percent,-0.1200,0,2000,lu_2011_tax_AP-EC-12,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-EC-6,Achat Prestations 6% - Extracommunautaire,AP-EC-6,purchase,percent,1,1,373,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-EC-6-p,Achat Prestations 6% - Extracommunautaire (+),AP-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AP-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AP-EC-6,lu_tax_code_template_t_AP-EC-6,-1,-1,lu_tax_code_template_b_AP-EC-6,lu_tax_code_template_t_AP-EC-6,lu_2011_chart_1 -lu_2011_tax_AP-EC-6-n,Achat Prestations 6% - Extracommunautaire (-),AP-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AP-EC-6,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-EC-6-p,Achat Prestations 6% - Extracommunautaire (+),AP-EC-6-p,purchase,percent,0.0600,0,1000,lu_2011_tax_AP-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AP-EC-6,lu_tax_code_template_t_AP-EC-6,-1,-1,lu_tax_code_template_b_AP-EC-6,lu_tax_code_template_t_AP-EC-6,lu_2011_chart_1 +lu_2011_tax_AP-EC-6-n,Achat Prestations 6% - Extracommunautaire (-),AP-EC-6-n,purchase,percent,-0.0600,0,2000,lu_2011_tax_AP-EC-6,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-EC-3,Achat Prestations 3% - Extracommunautaire,AP-EC-3,purchase,percent,1,1,374,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-EC-3-p,Achat Prestations 3% - Extracommunautaire (+),AP-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AP-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AP-EC-3,lu_tax_code_template_t_AP-EC-3,-1,-1,lu_tax_code_template_b_AP-EC-3,lu_tax_code_template_t_AP-EC-3,lu_2011_chart_1 -lu_2011_tax_AP-EC-3-n,Achat Prestations 3% - Extracommunautaire (-),AP-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AP-EC-3,lu_2011_account_4226113,lu_2011_account_4226113,-1,-1,,,1,1,,,lu_2011_chart_1 -lu_2011_tax_AP-EC-0,Achat Prestations 0% - Extracommunautaire,AP-EC-0,purchase,percent,0,0,375,,lu_2011_account_4226113,lu_2011_account_4226113,1,1,lu_tax_code_template_b_AP-EC-0,lu_tax_code_template_t_AP-EC-0,-1,-1,lu_tax_code_template_b_AP-EC-0,lu_tax_code_template_t_AP-EC-0,lu_2011_chart_1 +lu_2011_tax_AP-EC-3-p,Achat Prestations 3% - Extracommunautaire (+),AP-EC-3-p,purchase,percent,0.0300,0,1000,lu_2011_tax_AP-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AP-EC-3,lu_tax_code_template_t_AP-EC-3,-1,-1,lu_tax_code_template_b_AP-EC-3,lu_tax_code_template_t_AP-EC-3,lu_2011_chart_1 +lu_2011_tax_AP-EC-3-n,Achat Prestations 3% - Extracommunautaire (-),AP-EC-3-n,purchase,percent,-0.0300,0,2000,lu_2011_tax_AP-EC-3,lu_2011_account_4614113,lu_2011_account_4614113,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-EC-0,Achat Prestations 0% - Extracommunautaire,AP-EC-0,purchase,percent,0,0,375,,lu_2011_account_4614113,lu_2011_account_4614113,1,1,lu_tax_code_template_b_AP-EC-0,lu_tax_code_template_t_AP-EC-0,-1,-1,lu_tax_code_template_b_AP-EC-0,lu_tax_code_template_t_AP-EC-0,lu_2011_chart_1 lu_2011_tax_AP-TR-15,Achat Prestations 15% - Triangulaires,AP-TR-15,purchase,percent,1,1,471,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-TR-15,Achat Prestations 15% - Triangulaires (+),AP-TR-15,purchase,percent,0.1500,0,1000,lu_2011_tax_AP-TR-15,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AP-TR-15,lu_tax_code_template_t_AP-TR-15,-1,-1,lu_tax_code_template_b_AP-TR-15,lu_tax_code_template_t_AP-TR-15,lu_2011_chart_1 -lu_2011_tax_AP-TR-15,Achat Prestations 15% - Triangulaires (-),AP-TR-15,purchase,percent,-0.1500,0,2000,lu_2011_tax_AP-TR-15,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-TR-15,Achat Prestations 15% - Triangulaires (+),AP-TR-15,purchase,percent,0.1500,0,1000,lu_2011_tax_AP-TR-15,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AP-TR-15,lu_tax_code_template_t_AP-TR-15,-1,-1,lu_tax_code_template_b_AP-TR-15,lu_tax_code_template_t_AP-TR-15,lu_2011_chart_1 +lu_2011_tax_AP-TR-15,Achat Prestations 15% - Triangulaires (-),AP-TR-15,purchase,percent,-0.1500,0,2000,lu_2011_tax_AP-TR-15,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-TR-12,Achat Prestations 12% - Triangulaires,AP-TR-12,purchase,percent,1,1,472,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-TR-12,Achat Prestations 12% - Triangulaires (+),AP-TR-12,purchase,percent,0.1200,0,1000,lu_2011_tax_AP-TR-12,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AP-TR-12,lu_tax_code_template_t_AP-TR-12,-1,-1,lu_tax_code_template_b_AP-TR-12,lu_tax_code_template_t_AP-TR-12,lu_2011_chart_1 -lu_2011_tax_AP-TR-12,Achat Prestations 12% - Triangulaires (-),AP-TR-12,purchase,percent,-0.1200,0,2000,lu_2011_tax_AP-TR-12,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-TR-12,Achat Prestations 12% - Triangulaires (+),AP-TR-12,purchase,percent,0.1200,0,1000,lu_2011_tax_AP-TR-12,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AP-TR-12,lu_tax_code_template_t_AP-TR-12,-1,-1,lu_tax_code_template_b_AP-TR-12,lu_tax_code_template_t_AP-TR-12,lu_2011_chart_1 +lu_2011_tax_AP-TR-12,Achat Prestations 12% - Triangulaires (-),AP-TR-12,purchase,percent,-0.1200,0,2000,lu_2011_tax_AP-TR-12,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-TR-6,Achat Prestations 6% - Triangulaires,AP-TR-6,purchase,percent,1,1,473,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-TR-6,Achat Prestations 6% - Triangulaires (+),AP-TR-6,purchase,percent,0.0600,0,1000,lu_2011_tax_AP-TR-6,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AP-TR-6,lu_tax_code_template_t_AP-TR-6,-1,-1,lu_tax_code_template_b_AP-TR-6,lu_tax_code_template_t_AP-TR-6,lu_2011_chart_1 -lu_2011_tax_AP-TR-6,Achat Prestations 6% - Triangulaires (-),AP-TR-6,purchase,percent,-0.0600,0,2000,lu_2011_tax_AP-TR-6,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-TR-6,Achat Prestations 6% - Triangulaires (+),AP-TR-6,purchase,percent,0.0600,0,1000,lu_2011_tax_AP-TR-6,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AP-TR-6,lu_tax_code_template_t_AP-TR-6,-1,-1,lu_tax_code_template_b_AP-TR-6,lu_tax_code_template_t_AP-TR-6,lu_2011_chart_1 +lu_2011_tax_AP-TR-6,Achat Prestations 6% - Triangulaires (-),AP-TR-6,purchase,percent,-0.0600,0,2000,lu_2011_tax_AP-TR-6,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 lu_2011_tax_AP-TR-3,Achat Prestations 3% - Triangulaires,AP-TR-3,purchase,percent,1,1,475,,,,1,1,,,-1,-1,,,lu_2011_chart_1 -lu_2011_tax_AP-TR-3,Achat Prestations 3% - Triangulaires (+),AP-TR-3,purchase,percent,0.0300,0,1000,lu_2011_tax_AP-TR-3,lu_2011_account_4226114,lu_2011_account_4226114,1,1,lu_tax_code_template_b_AP-TR-3,lu_tax_code_template_t_AP-TR-3,-1,-1,lu_tax_code_template_b_AP-TR-3,lu_tax_code_template_t_AP-TR-3,lu_2011_chart_1 -lu_2011_tax_AP-TR-3,Achat Prestations 3% - Triangulaires (-),AP-TR-3,purchase,percent,-0.0300,0,2000,lu_2011_tax_AP-TR-3,lu_2011_account_4226114,lu_2011_account_4226114,-1,-1,,,1,1,,,lu_2011_chart_1 +lu_2011_tax_AP-TR-3,Achat Prestations 3% - Triangulaires (+),AP-TR-3,purchase,percent,0.0300,0,1000,lu_2011_tax_AP-TR-3,lu_2011_account_4614114,lu_2011_account_4614114,1,1,lu_tax_code_template_b_AP-TR-3,lu_tax_code_template_t_AP-TR-3,-1,-1,lu_tax_code_template_b_AP-TR-3,lu_tax_code_template_t_AP-TR-3,lu_2011_chart_1 +lu_2011_tax_AP-TR-3,Achat Prestations 3% - Triangulaires (-),AP-TR-3,purchase,percent,-0.0300,0,2000,lu_2011_tax_AP-TR-3,lu_2011_account_4614114,lu_2011_account_4614114,-1,-1,,,1,1,,,lu_2011_chart_1 diff --git a/addons/l10n_lu/account_chart_template.xml b/addons/l10n_lu/account_chart_template.xml index cb6c88527b48b..f0986277343be 100644 --- a/addons/l10n_lu/account_chart_template.xml +++ b/addons/l10n_lu/account_chart_template.xml @@ -14,13 +14,6 @@ - - PCMN Luxembourg - account.chart.template - default - - - diff --git a/addons/l10n_lu/images/config_chart_l10n_lu.jpeg b/addons/l10n_lu/images/config_chart_l10n_lu.jpeg new file mode 100644 index 0000000000000..303b02862d7f4 Binary files /dev/null and b/addons/l10n_lu/images/config_chart_l10n_lu.jpeg differ diff --git a/addons/l10n_lu/images/l10n_lu_chart.jpeg b/addons/l10n_lu/images/l10n_lu_chart.jpeg new file mode 100644 index 0000000000000..a537702ac12da Binary files /dev/null and b/addons/l10n_lu/images/l10n_lu_chart.jpeg differ diff --git a/addons/l10n_lu/wizard/__init__.pyc_dis b/addons/l10n_lu/wizard/__init__.pyc_dis new file mode 100644 index 0000000000000..0ca0019a6204c --- /dev/null +++ b/addons/l10n_lu/wizard/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_lu/wizard/__init__.py +import print_vat \ No newline at end of file diff --git a/addons/l10n_lu/wizard/print_vat.pyc_dis b/addons/l10n_lu/wizard/print_vat.pyc_dis new file mode 100644 index 0000000000000..b502915ddbb4e --- /dev/null +++ b/addons/l10n_lu/wizard/print_vat.pyc_dis @@ -0,0 +1,87 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_lu/wizard/print_vat.py +from __future__ import with_statement +from openerp.osv import fields, osv +from openerp import pooler +from openerp import tools +from openerp.tools.translate import _ +from openerp.report.render import render +from openerp.report.interface import report_int +from openerp import addons +import tempfile +import os + +class external_pdf(render): + + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type = 'pdf' + + def _render(self): + return self.pdf + + +class report_custom(report_int): + + def create(self, cr, uid, ids, datas, context = None): + pool = pooler.get_pool(cr.dbname) + taxobj = pool.get('account.tax.code') + if context is None: + context = {} + code_ids = taxobj.search(cr, uid, [('parent_id', 'child_of', [datas['form']['tax_code_id']])]) + result = {} + for t in taxobj.browse(cr, uid, code_ids, {'period_id': datas['form']['period_id']}): + if str(t.code): + result['case_' + str(t.code)] = '%.2f' % (t.sum_period or 0.0,) + + user = pool.get('res.users').browse(cr, uid, uid, context) + partner = user.company_id.partner_id + result['info_name'] = user.company_id.name + result['info_vatnum'] = partner.vat + if partner: + result['info_address'] = partner.street + result['info_address2'] = (partner.zip or '') + ' ' + (partner.city or '') + try: + tmp_file = tempfile.mkstemp('.pdf')[1] + try: + tools.pdf_utils.fill_pdf(addons.get_module_resource('l10n_lu', 'wizard', '2008_DECL_F_M10.pdf'), tmp_file, result) + with open(tmp_file, 'r') as ofile: + self.obj = external_pdf(ofile.read()) + finally: + try: + os.remove(tmp_file) + except: + pass + + self.obj.render() + return (self.obj.pdf, 'pdf') + except Exception: + raise osv.except_osv(_('PDF Not Created!'), _('Please check if package pdftk is installed!')) + + return + + +report_custom('report.l10n_lu.tax.report.print') + +class vat_declaration_report(osv.osv_memory): + _name = 'vat.declaration.report' + _description = 'VAT Declaration Report' + _columns = {'tax_code_id': fields.many2one('account.tax.code', 'Company', readonly=False, required=True, domain=[('parent_id', '=', False)]), + 'type': fields.selection([('monthly', 'Monthly'), ('quarterly', 'Quaterly'), ('yearly', 'Yearly')], 'Type', required=True), + 'period_id': fields.many2one('account.period', 'From Period', required=True), + 'to_period_id': fields.many2one('account.period', 'To Period', required=True)} + _defaults = {'type': 'monthly'} + + def print_vat_declaration_report(self, cr, uid, ids, context = None): + active_ids = context.get('active_ids', []) + data = {} + data['form'] = {} + data['ids'] = active_ids + data['form']['tax_code_id'] = self.browse(cr, uid, ids)[0].tax_code_id.id + data['form']['period_id'] = self.browse(cr, uid, ids)[0].period_id.id + return {'type': 'ir.actions.report.xml', + 'report_name': 'l10n_lu.tax.report.print', + 'datas': data} + + +vat_declaration_report() \ No newline at end of file diff --git a/addons/l10n_ma/__init__.pyc_dis b/addons/l10n_ma/__init__.pyc_dis new file mode 100644 index 0000000000000..9ed3638abbfe2 --- /dev/null +++ b/addons/l10n_ma/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_ma/__init__.py +import l10n_ma \ No newline at end of file diff --git a/addons/l10n_ma/images/config_chart_l10n_ma.jpeg b/addons/l10n_ma/images/config_chart_l10n_ma.jpeg new file mode 100644 index 0000000000000..356cbbcf0fc88 Binary files /dev/null and b/addons/l10n_ma/images/config_chart_l10n_ma.jpeg differ diff --git a/addons/l10n_ma/images/l10n_ma_chart.jpeg b/addons/l10n_ma/images/l10n_ma_chart.jpeg new file mode 100644 index 0000000000000..dd880fa18ecf5 Binary files /dev/null and b/addons/l10n_ma/images/l10n_ma_chart.jpeg differ diff --git a/addons/l10n_ma/l10n_ma.pyc_dis b/addons/l10n_ma/l10n_ma.pyc_dis new file mode 100644 index 0000000000000..9bc2326077cb8 --- /dev/null +++ b/addons/l10n_ma/l10n_ma.pyc_dis @@ -0,0 +1,25 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_ma/l10n_ma.py +from openerp.osv import fields, osv + +class l10n_ma_report(osv.osv): + _name = 'l10n.ma.report' + _description = 'Report for l10n_ma_kzc' + _columns = {'code': fields.char('Code', size=64), + 'name': fields.char('Name', size=128), + 'line_ids': fields.one2many('l10n.ma.line', 'report_id', 'Lines')} + _sql_constraints = [('code_uniq', 'unique (code)', 'The code report must be unique !')] + + +l10n_ma_report() + +class l10n_ma_line(osv.osv): + _name = 'l10n.ma.line' + _description = 'Report Lines for l10n_ma' + _columns = {'code': fields.char('Variable Name', size=64), + 'definition': fields.char('Definition', size=512), + 'name': fields.char('Name', size=256), + 'report_id': fields.many2one('l10n.ma.report', 'Report')} + _sql_constraints = [('code_uniq', 'unique (code)', 'The variable name must be unique !')] + + +l10n_ma_line() \ No newline at end of file diff --git a/addons/l10n_ma/l10n_ma_tax.xml b/addons/l10n_ma/l10n_ma_tax.xml index 17aba7ed07ae4..070d2d4fc9bd2 100644 --- a/addons/l10n_ma/l10n_ma_tax.xml +++ b/addons/l10n_ma/l10n_ma_tax.xml @@ -738,19 +738,11 @@ - Plan comptable marocain + compta Kazacube - - - - - Plan comptable marocain - account.chart.template - default - - + diff --git a/addons/l10n_mx/__init__.pyc_dis b/addons/l10n_mx/__init__.pyc_dis new file mode 100644 index 0000000000000..272f2880e0544 --- /dev/null +++ b/addons/l10n_mx/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_mx/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_mx/data/account_chart.xml b/addons/l10n_mx/data/account_chart.xml index 477b3353b8e2c..491942e4b7231 100644 --- a/addons/l10n_mx/data/account_chart.xml +++ b/addons/l10n_mx/data/account_chart.xml @@ -3693,14 +3693,9 @@ Cuentas del plan + + - - Plan de Cuentas para Mexico - account.chart.template - default - - - diff --git a/addons/l10n_mx/images/config_chart_l10n_mx.jpeg b/addons/l10n_mx/images/config_chart_l10n_mx.jpeg new file mode 100644 index 0000000000000..6bc376db806f8 Binary files /dev/null and b/addons/l10n_mx/images/config_chart_l10n_mx.jpeg differ diff --git a/addons/l10n_mx/images/l10n_mx_chart.jpeg b/addons/l10n_mx/images/l10n_mx_chart.jpeg new file mode 100644 index 0000000000000..9029b6bd341c5 Binary files /dev/null and b/addons/l10n_mx/images/l10n_mx_chart.jpeg differ diff --git a/addons/l10n_nl/__init__.pyc_dis b/addons/l10n_nl/__init__.pyc_dis new file mode 100644 index 0000000000000..ff5864a6d204f --- /dev/null +++ b/addons/l10n_nl/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_nl/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_nl/account_chart_netherlands.xml b/addons/l10n_nl/account_chart_netherlands.xml index 1599d3df1b95b..c7eefb42cde31 100644 --- a/addons/l10n_nl/account_chart_netherlands.xml +++ b/addons/l10n_nl/account_chart_netherlands.xml @@ -4103,13 +4103,6 @@ - - Nederlands Grootboekschema - account.chart.template - default - - - - - - Romania - Chart of Accounts - account.chart.template - default - - - diff --git a/addons/l10n_ro/images/config_chart_l10n_ro.jpeg b/addons/l10n_ro/images/config_chart_l10n_ro.jpeg new file mode 100644 index 0000000000000..4df700058f7a2 Binary files /dev/null and b/addons/l10n_ro/images/config_chart_l10n_ro.jpeg differ diff --git a/addons/l10n_ro/images/l10n_ro_chart.jpeg b/addons/l10n_ro/images/l10n_ro_chart.jpeg new file mode 100644 index 0000000000000..eeed2a5e7bc5f Binary files /dev/null and b/addons/l10n_ro/images/l10n_ro_chart.jpeg differ diff --git a/addons/l10n_ro/res_partner.pyc_dis b/addons/l10n_ro/res_partner.pyc_dis new file mode 100644 index 0000000000000..e33ab16e14954 --- /dev/null +++ b/addons/l10n_ro/res_partner.pyc_dis @@ -0,0 +1,19 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_ro/res_partner.py +from openerp.osv import fields, osv + +class res_partner(osv.osv): + _name = 'res.partner' + _inherit = 'res.partner' + _columns = {'nrc': fields.char('NRC', size=16, help='Registration number at the Registry of Commerce')} + _sql_constraints = [('vat_uniq', 'unique (id)', 'The vat of the partner must be unique !'), ('nrc_uniq', 'unique (id)', 'The code of the partner must be unique !')] + + def _auto_init(self, cr, context = None): + result = super(res_partner, self)._auto_init(cr, context=context) + cr.execute('\n DROP INDEX IF EXISTS res_partner_vat_uniq_for_companies;\n DROP INDEX IF EXISTS res_partner_nrc_uniq_for_companies;\n CREATE UNIQUE INDEX res_partner_vat_uniq_for_companies ON res_partner (vat) WHERE is_company OR parent_id IS NULL;\n CREATE UNIQUE INDEX res_partner_nrc_uniq_for_companies ON res_partner (nrc) WHERE is_company OR parent_id IS NULL;\n ') + return result + + def _commercial_fields(self, cr, uid, context = None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['nrc'] + + +res_partner() \ No newline at end of file diff --git a/addons/l10n_si/__init__.pyc_dis b/addons/l10n_si/__init__.pyc_dis new file mode 100644 index 0000000000000..b6648458cfec1 --- /dev/null +++ b/addons/l10n_si/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_si/__init__.py +import account_wizard \ No newline at end of file diff --git a/addons/l10n_si/account_wizard.pyc_dis b/addons/l10n_si/account_wizard.pyc_dis new file mode 100644 index 0000000000000..a68003c676d17 --- /dev/null +++ b/addons/l10n_si/account_wizard.pyc_dis @@ -0,0 +1,11 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_si/account_wizard.py +import tools +from osv import osv +import addons + +class AccountWizard_cd(osv.osv_memory): + _inherit = 'wizard.multi.charts.accounts' + _defaults = {'code_digits': 6} + + +AccountWizard_cd() \ No newline at end of file diff --git a/addons/l10n_syscohada/l10n_syscohada_data.xml b/addons/l10n_syscohada/l10n_syscohada_data.xml index 99a1cbdd7fd36..d90988e00e381 100644 --- a/addons/l10n_syscohada/l10n_syscohada_data.xml +++ b/addons/l10n_syscohada/l10n_syscohada_data.xml @@ -1850,13 +1850,6 @@ - - - SYSCOHADA - Plan de compte - account.chart.template - default - - diff --git a/addons/l10n_th/__init__.pyc_dis b/addons/l10n_th/__init__.pyc_dis new file mode 100644 index 0000000000000..bdc949af6023f --- /dev/null +++ b/addons/l10n_th/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_th/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_th/account_data.xml b/addons/l10n_th/account_data.xml index 91e6969801b77..9ae3d9e33641e 100644 --- a/addons/l10n_th/account_data.xml +++ b/addons/l10n_th/account_data.xml @@ -478,13 +478,6 @@ - - Thailand - Chart of Accounts - account.chart.template - default - - - diff --git a/addons/l10n_th/images/config_chart_l10n_th.jpeg b/addons/l10n_th/images/config_chart_l10n_th.jpeg new file mode 100644 index 0000000000000..128136e2f6c41 Binary files /dev/null and b/addons/l10n_th/images/config_chart_l10n_th.jpeg differ diff --git a/addons/l10n_th/images/l10n_th_chart.jpeg b/addons/l10n_th/images/l10n_th_chart.jpeg new file mode 100644 index 0000000000000..9c2380995c9b2 Binary files /dev/null and b/addons/l10n_th/images/l10n_th_chart.jpeg differ diff --git a/addons/l10n_tr/__init__.pyc_dis b/addons/l10n_tr/__init__.pyc_dis new file mode 100644 index 0000000000000..9743c6fed4c7e --- /dev/null +++ b/addons/l10n_tr/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_tr/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_tr/account_chart_template.xml b/addons/l10n_tr/account_chart_template.xml index 11596155fd32e..3e10353298e7f 100644 --- a/addons/l10n_tr/account_chart_template.xml +++ b/addons/l10n_tr/account_chart_template.xml @@ -13,13 +13,6 @@ - - Tek Düzen Hesap Planı - account.chart.template - default - - - diff --git a/addons/l10n_tr/images/chart_l10n_tr_1.jpg b/addons/l10n_tr/images/chart_l10n_tr_1.jpg new file mode 100644 index 0000000000000..eff22ec03821f Binary files /dev/null and b/addons/l10n_tr/images/chart_l10n_tr_1.jpg differ diff --git a/addons/l10n_tr/images/chart_l10n_tr_2.jpg b/addons/l10n_tr/images/chart_l10n_tr_2.jpg new file mode 100644 index 0000000000000..463893237be1a Binary files /dev/null and b/addons/l10n_tr/images/chart_l10n_tr_2.jpg differ diff --git a/addons/l10n_tr/images/chart_l10n_tr_3.jpg b/addons/l10n_tr/images/chart_l10n_tr_3.jpg new file mode 100644 index 0000000000000..1719a9659ebe6 Binary files /dev/null and b/addons/l10n_tr/images/chart_l10n_tr_3.jpg differ diff --git a/addons/l10n_uk/__init__.pyc_dis b/addons/l10n_uk/__init__.pyc_dis new file mode 100644 index 0000000000000..c6bbb0d42d560 --- /dev/null +++ b/addons/l10n_uk/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_uk/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_uk/images/config_chart_l10n_uk.jpeg b/addons/l10n_uk/images/config_chart_l10n_uk.jpeg new file mode 100644 index 0000000000000..d17a45d6e256b Binary files /dev/null and b/addons/l10n_uk/images/config_chart_l10n_uk.jpeg differ diff --git a/addons/l10n_uk/images/l10n_uk_chart.jpeg b/addons/l10n_uk/images/l10n_uk_chart.jpeg new file mode 100644 index 0000000000000..f3c81e71db542 Binary files /dev/null and b/addons/l10n_uk/images/l10n_uk_chart.jpeg differ diff --git a/addons/l10n_us/__init__.pyc_dis b/addons/l10n_us/__init__.pyc_dis new file mode 100644 index 0000000000000..6cd335d704a36 --- /dev/null +++ b/addons/l10n_us/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/lamthao/server/openerp/addons/l10n_us/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_us/account_chart_template.xml b/addons/l10n_us/account_chart_template.xml index ac2784e33d7a1..0863680981fcd 100644 --- a/addons/l10n_us/account_chart_template.xml +++ b/addons/l10n_us/account_chart_template.xml @@ -7,13 +7,6 @@ - - Basic Chart of Account - account.chart.template - default - - - Cost of Goods sold @@ -21,103 +14,44 @@ - - Cost of Goods sold - account.chart.template - default - - - - Advertising - - Advertising - account.chart.template - default - - - - Agriculture - - Agriculture - account.chart.template - default - - - - Construction Trades (Plumber, Electrician, HVAC, etc.) - - Construction Trades (Plumber, Electrician, HVAC, etc.) - account.chart.template - default - - - Financial Services other than Accounting or Bookkeeping - - Financial Services other than Accounting or Bookkeeping - account.chart.template - default - - - General Service-Based Business - - General Service-Based Business - account.chart.template - default - - - Legal Services - - Legal Services - account.chart.template - default - - - General Product-Based Business - - General Product-Based Business - account.chart.template - default - - - diff --git a/addons/l10n_us/account_chart_template_after.xml b/addons/l10n_us/account_chart_template_after.xml index 4dea930f09f43..5ca3a169abaf1 100644 --- a/addons/l10n_us/account_chart_template_after.xml +++ b/addons/l10n_us/account_chart_template_after.xml @@ -6,8 +6,6 @@ - - diff --git a/addons/l10n_uy/__init__.pyc_dis b/addons/l10n_uy/__init__.pyc_dis new file mode 100644 index 0000000000000..7479e5d355d18 --- /dev/null +++ b/addons/l10n_uy/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_uy/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_uy/account_chart_template.xml b/addons/l10n_uy/account_chart_template.xml index 05868963d49c5..4ce3518496700 100644 --- a/addons/l10n_uy/account_chart_template.xml +++ b/addons/l10n_uy/account_chart_template.xml @@ -1896,13 +1896,7 @@ - - Plan de Cuentas Uruguay - Template - account.chart.template - default - - - + diff --git a/addons/l10n_uy/images/config_chart_l10n_uy.jpeg b/addons/l10n_uy/images/config_chart_l10n_uy.jpeg new file mode 100644 index 0000000000000..78276c2e2dd7b Binary files /dev/null and b/addons/l10n_uy/images/config_chart_l10n_uy.jpeg differ diff --git a/addons/l10n_uy/images/l10n_uy_chart.jpeg b/addons/l10n_uy/images/l10n_uy_chart.jpeg new file mode 100644 index 0000000000000..5d605f8e28c81 Binary files /dev/null and b/addons/l10n_uy/images/l10n_uy_chart.jpeg differ diff --git a/addons/l10n_ve/__init__.pyc_dis b/addons/l10n_ve/__init__.pyc_dis new file mode 100644 index 0000000000000..d283b48d9eb31 --- /dev/null +++ b/addons/l10n_ve/__init__.pyc_dis @@ -0,0 +1,2 @@ +# Embedded file name: /opt/openerp/server/openerp/addons/l10n_ve/__init__.py +pass \ No newline at end of file diff --git a/addons/l10n_ve/data/account_chart.xml b/addons/l10n_ve/data/account_chart.xml index c4725b117bd1b..05645805a8447 100644 --- a/addons/l10n_ve/data/account_chart.xml +++ b/addons/l10n_ve/data/account_chart.xml @@ -3316,12 +3316,5 @@ - - Venezuelan - Account - account.chart.template - default - - - diff --git a/addons/l10n_ve/data/account_tax.xml b/addons/l10n_ve/data/account_tax.xml index 8fb1aa4b819cc..d28b0d1d8f3e4 100644 --- a/addons/l10n_ve/data/account_tax.xml +++ b/addons/l10n_ve/data/account_tax.xml @@ -1,14 +1,14 @@ - + Exento 0.00000 percent - all - - + sale + + @@ -21,8 +21,8 @@ 0.120000 percent sale - - + + @@ -34,8 +34,8 @@ 0.080000 percent sale - - + + @@ -47,21 +47,34 @@ 0.220000 percent sale - - + + + + + Exento + 0.00000 + percent + purchase + + + + + + + IVA (12.0%) compras 0.120000 percent purchase - - + + @@ -73,8 +86,8 @@ 0.080000 percent purchase - - + + @@ -86,8 +99,8 @@ 0.220000 percent purchase - - + + diff --git a/addons/l10n_ve/images/config_chart_l10n_ve.jpeg b/addons/l10n_ve/images/config_chart_l10n_ve.jpeg new file mode 100644 index 0000000000000..d6dea38e89a5f Binary files /dev/null and b/addons/l10n_ve/images/config_chart_l10n_ve.jpeg differ diff --git a/addons/l10n_ve/images/l10n_ve_chart.jpeg b/addons/l10n_ve/images/l10n_ve_chart.jpeg new file mode 100644 index 0000000000000..c90c58973001a Binary files /dev/null and b/addons/l10n_ve/images/l10n_ve_chart.jpeg differ diff --git a/addons/l10n_vn/account_chart.xml b/addons/l10n_vn/account_chart.xml old mode 100755 new mode 100644 index c09512c8de6cd..c7d3f6a9ee353 --- a/addons/l10n_vn/account_chart.xml +++ b/addons/l10n_vn/account_chart.xml @@ -2017,13 +2017,6 @@ - - VN - Chart of Accounts - account.chart.template - default - - - diff --git a/addons/l10n_vn/account_tax.xml b/addons/l10n_vn/account_tax.xml old mode 100755 new mode 100644 diff --git a/addons/l10n_vn/account_tax_code.xml b/addons/l10n_vn/account_tax_code.xml old mode 100755 new mode 100644 diff --git a/addons/l10n_vn/l10n_vn_wizard.xml b/addons/l10n_vn/l10n_vn_wizard.xml old mode 100755 new mode 100644 diff --git a/addons/l10n_vn/static/src/img/icon.png b/addons/l10n_vn/static/src/img/icon.png old mode 100755 new mode 100644 diff --git a/addons/lunch/images/alert.jpeg b/addons/lunch/images/alert.jpeg new file mode 100644 index 0000000000000..e54479c298784 Binary files /dev/null and b/addons/lunch/images/alert.jpeg differ diff --git a/addons/lunch/images/lunch_account.jpeg b/addons/lunch/images/lunch_account.jpeg new file mode 100644 index 0000000000000..b46b726393273 Binary files /dev/null and b/addons/lunch/images/lunch_account.jpeg differ diff --git a/addons/lunch/images/new_order.jpeg b/addons/lunch/images/new_order.jpeg new file mode 100644 index 0000000000000..738387c0604a9 Binary files /dev/null and b/addons/lunch/images/new_order.jpeg differ diff --git a/addons/lunch/images/order_by_supplier_analysis.jpeg b/addons/lunch/images/order_by_supplier_analysis.jpeg new file mode 100644 index 0000000000000..27873d9803fa4 Binary files /dev/null and b/addons/lunch/images/order_by_supplier_analysis.jpeg differ diff --git a/addons/lunch/static/src/css/Makefile b/addons/lunch/static/src/css/Makefile new file mode 100644 index 0000000000000..941e8dbfe7d6a --- /dev/null +++ b/addons/lunch/static/src/css/Makefile @@ -0,0 +1,3 @@ +lunch.css: lunch.sass + sass -t expanded lunch.sass lunch.css + diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 47a77e0d9cbf3..63cd0888986d7 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -81,19 +81,16 @@ 'css': [ 'static/src/css/mail.css', 'static/src/css/mail_group.css', - 'static/src/css/announcement.css', ], 'js': [ 'static/lib/jquery.expander/jquery.expander.js', 'static/src/js/mail.js', 'static/src/js/mail_followers.js', 'static/src/js/many2many_tags_email.js', - 'static/src/js/announcement.js', ], 'qweb': [ 'static/src/xml/mail.xml', 'static/src/xml/mail_followers.xml', - 'static/src/xml/announcement.xml', ], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py index e6b0b7cb3c653..e90d2b558603d 100644 --- a/addons/mail/controllers/main.py +++ b/addons/mail/controllers/main.py @@ -1,24 +1,37 @@ -import base64 -import openerp.addons.web.http as oeweb -from openerp.addons.web.controllers.main import content_disposition -import mimetypes - -#---------------------------------------------------------- -# Controller -#---------------------------------------------------------- -class MailController(oeweb.Controller): - _cp_path = '/mail' - - @oeweb.httprequest - def download_attachment(self, req, model, id, method, attachment_id, **kw): - Model = req.session.model(model) - res = getattr(Model, method)(int(id), int(attachment_id)) - if res: - filecontent = base64.b64decode(res.get('base64')) - filename = res.get('filename') - content_type = mimetypes.guess_type(filename) - if filecontent and filename: - return req.make_response(filecontent, - headers=[('Content-Type', content_type[0] or 'application/octet-stream'), - ('Content-Disposition', content_disposition(filename, req))]) - return req.not_found() +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import base64 +import openerp.addons.web.http as oeweb +from openerp.addons.web.controllers.main import content_disposition + +class MailController(oeweb.Controller): + _cp_path = '/mail' + + @oeweb.httprequest + def download_attachment(self, req, model, id, method, attachment_id, **kw): + Model = req.session.model(model) + res = getattr(Model, method)(int(id), int(attachment_id)) + if res: + filecontent = base64.b64decode(res.get('base64')) + filename = res.get('filename') + if filecontent and filename: + return req.make_response(filecontent, headers=[('Content-Type', 'application/octet-stream'), ('Content-Disposition', content_disposition(filename, req))]) + return req.not_found() \ No newline at end of file diff --git a/addons/mail/data/mail_group_data.xml b/addons/mail/data/mail_group_data.xml index d61c883f44744..287121aa57d32 100644 --- a/addons/mail/data/mail_group_data.xml +++ b/addons/mail/data/mail_group_data.xml @@ -2,13 +2,13 @@ - + - + diff --git a/addons/mail/images/email.jpeg b/addons/mail/images/email.jpeg new file mode 100644 index 0000000000000..0c2d6c029ce4f Binary files /dev/null and b/addons/mail/images/email.jpeg differ diff --git a/addons/mail/images/inbox.jpeg b/addons/mail/images/inbox.jpeg new file mode 100644 index 0000000000000..7fc6f76030063 Binary files /dev/null and b/addons/mail/images/inbox.jpeg differ diff --git a/addons/mail/images/join_a_group.jpeg b/addons/mail/images/join_a_group.jpeg new file mode 100644 index 0000000000000..de4affe48781a Binary files /dev/null and b/addons/mail/images/join_a_group.jpeg differ diff --git a/addons/mail/images/messages_form.jpeg b/addons/mail/images/messages_form.jpeg new file mode 100644 index 0000000000000..7781eac0f9ca5 Binary files /dev/null and b/addons/mail/images/messages_form.jpeg differ diff --git a/addons/mail/images/messages_list.jpeg b/addons/mail/images/messages_list.jpeg new file mode 100644 index 0000000000000..fa625693cd058 Binary files /dev/null and b/addons/mail/images/messages_list.jpeg differ diff --git a/addons/mail/images/share_a_message.jpeg b/addons/mail/images/share_a_message.jpeg new file mode 100644 index 0000000000000..8af842a281714 Binary files /dev/null and b/addons/mail/images/share_a_message.jpeg differ diff --git a/addons/mail/mail_alias.py b/addons/mail/mail_alias.py index 6366430a234c7..da1585a0992fb 100644 --- a/addons/mail/mail_alias.py +++ b/addons/mail/mail_alias.py @@ -1,206 +1,160 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (C) 2012 OpenERP S.A. (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import logging -import re -import unicodedata - -from openerp.osv import fields, osv -from openerp.tools import ustr -from openerp.modules.registry import RegistryManager -from openerp import SUPERUSER_ID -from openerp.tools.safe_eval import safe_eval as eval - -_logger = logging.getLogger(__name__) - -# Inspired by http://stackoverflow.com/questions/517923 -def remove_accents(input_str): - """Suboptimal-but-better-than-nothing way to replace accented - latin letters by an ASCII equivalent. Will obviously change the - meaning of input_str and work only for some cases""" - input_str = ustr(input_str) - nkfd_form = unicodedata.normalize('NFKD', input_str) - return u''.join([c for c in nkfd_form if not unicodedata.combining(c)]) - -class mail_alias(osv.Model): - """A Mail Alias is a mapping of an email address with a given OpenERP Document - model. It is used by OpenERP's mail gateway when processing incoming emails - sent to the system. If the recipient address (To) of the message matches - a Mail Alias, the message will be either processed following the rules - of that alias. If the message is a reply it will be attached to the - existing discussion on the corresponding record, otherwise a new - record of the corresponding model will be created. - - This is meant to be used in combination with a catch-all email configuration - on the company's mail server, so that as soon as a new mail.alias is - created, it becomes immediately usable and OpenERP will accept email for it. - """ - _name = 'mail.alias' - _description = "Email Aliases" - _rec_name = 'alias_name' - _order = 'alias_model_id, alias_name' - - def _get_alias_domain(self, cr, uid, ids, name, args, context=None): - ir_config_parameter = self.pool.get("ir.config_parameter") - domain = ir_config_parameter.get_param(cr, SUPERUSER_ID, "mail.catchall.domain", context=context) - return dict.fromkeys(ids, domain or "") - - _columns = { - 'alias_name': fields.char('Alias', required=True, - help="The name of the email alias, e.g. 'jobs' " - "if you want to catch emails for ",), - 'alias_model_id': fields.many2one('ir.model', 'Aliased Model', required=True, ondelete="cascade", - help="The model (OpenERP Document Kind) to which this alias " - "corresponds. Any incoming email that does not reply to an " - "existing record will cause the creation of a new record " - "of this model (e.g. a Project Task)", - # hack to only allow selecting mail_thread models (we might - # (have a few false positives, though) - domain="[('field_id.name', '=', 'message_ids')]"), - 'alias_user_id': fields.many2one('res.users', 'Owner', - help="The owner of records created upon receiving emails on this alias. " - "If this field is not set the system will attempt to find the right owner " - "based on the sender (From) address, or will use the Administrator account " - "if no system user is found for that address."), - 'alias_defaults': fields.text('Default Values', required=True, - help="A Python dictionary that will be evaluated to provide " - "default values when creating new records for this alias."), - 'alias_force_thread_id': fields.integer('Record Thread ID', - help="Optional ID of a thread (record) to which all incoming " - "messages will be attached, even if they did not reply to it. " - "If set, this will disable the creation of new records completely."), - 'alias_domain': fields.function(_get_alias_domain, string="Alias domain", type='char', size=None), - } - - _defaults = { - 'alias_defaults': '{}', - 'alias_user_id': lambda self,cr,uid,context: uid, - # looks better when creating new aliases - even if the field is informative only - 'alias_domain': lambda self,cr,uid,context: self._get_alias_domain(cr, SUPERUSER_ID,[1],None,None)[1] - } - - _sql_constraints = [ - ('alias_unique', 'UNIQUE(alias_name)', 'Unfortunately this email alias is already used, please choose a unique one') - ] - - def _check_alias_defaults(self, cr, uid, ids, context=None): - try: - for record in self.browse(cr, uid, ids, context=context): - dict(eval(record.alias_defaults)) - except Exception: - return False - return True - - _constraints = [ - (_check_alias_defaults, '''Invalid expression, it must be a literal python dictionary definition e.g. "{'field': 'value'}"''', ['alias_defaults']), - ] - - def name_get(self, cr, uid, ids, context=None): - """Return the mail alias display alias_name, inclusing the implicit - mail catchall domain from config. - e.g. `jobs@openerp.my.openerp.com` or `sales@openerp.my.openerp.com` - """ - return [(record['id'], "%s@%s" % (record['alias_name'], record['alias_domain'])) - for record in self.read(cr, uid, ids, ['alias_name', 'alias_domain'], context=context)] - - def _find_unique(self, cr, uid, name, context=None): - """Find a unique alias name similar to ``name``. If ``name`` is - already taken, make a variant by adding an integer suffix until - an unused alias is found. - """ - sequence = None - while True: - new_name = "%s%s" % (name, sequence) if sequence is not None else name - if not self.search(cr, uid, [('alias_name', '=', new_name)]): - break - sequence = (sequence + 1) if sequence else 2 - return new_name - - def migrate_to_alias(self, cr, child_model_name, child_table_name, child_model_auto_init_fct, - alias_id_column, alias_key, alias_prefix='', alias_force_key='', alias_defaults={}, context=None): - """ Installation hook to create aliases for all users and avoid constraint errors. - - :param child_model_name: model name of the child class (i.e. res.users) - :param child_table_name: table name of the child class (i.e. res_users) - :param child_model_auto_init_fct: pointer to the _auto_init function - (i.e. super(res_users,self)._auto_init(cr, context=context)) - :param alias_id_column: alias_id column (i.e. self._columns['alias_id']) - :param alias_key: name of the column used for the unique name (i.e. 'login') - :param alias_prefix: prefix for the unique name (i.e. 'jobs' + ...) - :param alias_force_key': name of the column for force_thread_id; - if empty string, not taken into account - :param alias_defaults: dict, keys = mail.alias columns, values = child - model column name used for default values (i.e. {'job_id': 'id'}) - """ - if context is None: - context = {} - - # disable the unique alias_id not null constraint, to avoid spurious warning during - # super.auto_init. We'll reinstall it afterwards. - alias_id_column.required = False - - # call _auto_init - result = child_model_auto_init_fct(cr, context=context) - - registry = RegistryManager.get(cr.dbname) - mail_alias = registry.get('mail.alias') - child_class_model = registry.get(child_model_name) - no_alias_ids = child_class_model.search(cr, SUPERUSER_ID, [('alias_id', '=', False)], context={'active_test': False}) - # Use read() not browse(), to avoid prefetching uninitialized inherited fields - for obj_data in child_class_model.read(cr, SUPERUSER_ID, no_alias_ids, [alias_key]): - alias_vals = {'alias_name': '%s%s' % (alias_prefix, obj_data[alias_key])} - if alias_force_key: - alias_vals['alias_force_thread_id'] = obj_data[alias_force_key] - alias_vals['alias_defaults'] = dict((k, obj_data[v]) for k, v in alias_defaults.iteritems()) - alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, alias_vals, model_name=context.get('alias_model_name', child_model_name)) - child_class_model.write(cr, SUPERUSER_ID, obj_data['id'], {'alias_id': alias_id}) - _logger.info('Mail alias created for %s %s (uid %s)', child_model_name, obj_data[alias_key], obj_data['id']) - - # Finally attempt to reinstate the missing constraint - try: - cr.execute('ALTER TABLE %s ALTER COLUMN alias_id SET NOT NULL' % (child_table_name)) - except Exception: - _logger.warning("Table '%s': unable to set a NOT NULL constraint on column '%s' !\n"\ - "If you want to have it, you should update the records and execute manually:\n"\ - "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", - child_table_name, 'alias_id', child_table_name, 'alias_id') - - # set back the unique alias_id constraint - alias_id_column.required = True - - return result - - def create_unique_alias(self, cr, uid, vals, model_name=None, context=None): - """Creates an email.alias record according to the values provided in ``vals``, - with 2 alterations: the ``alias_name`` value may be suffixed in order to - make it unique (and certain unsafe characters replaced), and - he ``alias_model_id`` value will set to the model ID of the ``model_name`` - value, if provided, - """ - # when an alias name appears to already be an email, we keep the local part only - alias_name = remove_accents(vals['alias_name']).lower().split('@')[0] - alias_name = re.sub(r'[^\w+.]+', '-', alias_name) - alias_name = self._find_unique(cr, uid, alias_name, context=context) - vals['alias_name'] = alias_name - if model_name: - model_id = self.pool.get('ir.model').search(cr, uid, [('model', '=', model_name)], context=context)[0] - vals['alias_model_id'] = model_id - return self.create(cr, uid, vals, context=context) +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import logging +import re +import unicodedata +from openerp.osv import fields, osv +from openerp.tools import ustr +from openerp.modules.registry import RegistryManager +from openerp import SUPERUSER_ID +_logger = logging.getLogger(__name__) + +def remove_accents(input_str): + """Suboptimal-but-better-than-nothing way to replace accented + latin letters by an ASCII equivalent. Will obviously change the + meaning of input_str and work only for some cases""" + input_str = ustr(input_str) + nkfd_form = unicodedata.normalize('NFKD', input_str) + return u''.join([ c for c in nkfd_form if not unicodedata.combining(c) ]) + + +class mail_alias(osv.Model): + """A Mail Alias is a mapping of an email address with a given OpenERP Document + model. It is used by OpenERP's mail gateway when processing incoming emails + sent to the system. If the recipient address (To) of the message matches + a Mail Alias, the message will be either processed following the rules + of that alias. If the message is a reply it will be attached to the + existing discussion on the corresponding record, otherwise a new + record of the corresponding model will be created. + + This is meant to be used in combination with a catch-all email configuration + on the company's mail server, so that as soon as a new mail.alias is + created, it becomes immediately usable and OpenERP will accept email for it. + """ + _name = 'mail.alias' + _description = 'Email Aliases' + _rec_name = 'alias_name' + _order = 'alias_model_id, alias_name' + + def _get_alias_domain(self, cr, uid, ids, name, args, context = None): + ir_config_parameter = self.pool.get('ir.config_parameter') + domain = ir_config_parameter.get_param(cr, uid, 'mail.catchall.domain', context=context) + return dict.fromkeys(ids, domain or '') + + _columns = {'alias_name': fields.char('Alias', required=True, help="The name of the email alias, e.g. 'jobs' if you want to catch emails for "), + 'alias_model_id': fields.many2one('ir.model', 'Aliased Model', required=True, ondelete='cascade', help='The model (OpenERP Document Kind) to which this alias corresponds. Any incoming email that does not reply to an existing record will cause the creation of a new record of this model (e.g. a Project Task)', domain="[('field_id.name', '=', 'message_ids')]"), + 'alias_user_id': fields.many2one('res.users', 'Owner', help='The owner of records created upon receiving emails on this alias. If this field is not set the system will attempt to find the right owner based on the sender (From) address, or will use the Administrator account if no system user is found for that address.'), + 'alias_defaults': fields.text('Default Values', required=True, help='A Python dictionary that will be evaluated to provide default values when creating new records for this alias.'), + 'alias_force_thread_id': fields.integer('Record Thread ID', help='Optional ID of a thread (record) to which all incoming messages will be attached, even if they did not reply to it. If set, this will disable the creation of new records completely.'), + 'alias_domain': fields.function(_get_alias_domain, string='Alias domain', type='char', size=None)} + _defaults = {'alias_defaults': '{}', + 'alias_user_id': lambda self, cr, uid, context: uid, + 'alias_domain': lambda self, cr, uid, context: self._get_alias_domain(cr, SUPERUSER_ID, [1], None, None)[1]} + _sql_constraints = [('alias_unique', 'UNIQUE(alias_name)', 'Unfortunately this email alias is already used, please choose a unique one')] + + def _check_alias_defaults(self, cr, uid, ids, context = None): + try: + for record in self.browse(cr, uid, ids, context=context): + dict(eval(record.alias_defaults)) + + except Exception: + return False + + return True + + _constraints = [(_check_alias_defaults, 'Invalid expression, it must be a literal python dictionary definition e.g. "{\'field\': \'value\'}"', ['alias_defaults'])] + + def name_get(self, cr, uid, ids, context = None): + """Return the mail alias display alias_name, inclusing the implicit + mail catchall domain from config. + e.g. `jobs@openerp.my.openerp.com` or `sales@openerp.my.openerp.com` + """ + return [ (record['id'], '%s@%s' % (record['alias_name'], record['alias_domain'])) for record in self.read(cr, uid, ids, ['alias_name', 'alias_domain'], context=context) ] + + def _find_unique(self, cr, uid, name, context = None): + """Find a unique alias name similar to ``name``. If ``name`` is + already taken, make a variant by adding an integer suffix until + an unused alias is found. + """ + sequence = None + while True: + new_name = '%s%s' % (name, sequence) if sequence is not None else name + if not self.search(cr, uid, [('alias_name', '=', new_name)]): + break + sequence = sequence + 1 if sequence else 2 + + return new_name + + def migrate_to_alias(self, cr, child_model_name, child_table_name, child_model_auto_init_fct, alias_id_column, alias_key, alias_prefix = '', alias_force_key = '', alias_defaults = {}, context = None): + """ Installation hook to create aliases for all users and avoid constraint errors. + + :param child_model_name: model name of the child class (i.e. res.users) + :param child_table_name: table name of the child class (i.e. res_users) + :param child_model_auto_init_fct: pointer to the _auto_init function + (i.e. super(res_users,self)._auto_init(cr, context=context)) + :param alias_id_column: alias_id column (i.e. self._columns['alias_id']) + :param alias_key: name of the column used for the unique name (i.e. 'login') + :param alias_prefix: prefix for the unique name (i.e. 'jobs' + ...) + :param alias_force_key': name of the column for force_thread_id; + if empty string, not taken into account + :param alias_defaults: dict, keys = mail.alias columns, values = child + model column name used for default values (i.e. {'job_id': 'id'}) + """ + if context is None: + context = {} + alias_id_column.required = False + child_model_auto_init_fct(cr, context=context) + registry = RegistryManager.get(cr.dbname) + mail_alias = registry.get('mail.alias') + child_class_model = registry.get(child_model_name) + no_alias_ids = child_class_model.search(cr, SUPERUSER_ID, [('alias_id', '=', False)], context={'active_test': False}) + for obj_data in child_class_model.read(cr, SUPERUSER_ID, no_alias_ids, [alias_key]): + alias_vals = {'alias_name': '%s%s' % (alias_prefix, obj_data[alias_key])} + if alias_force_key: + alias_vals['alias_force_thread_id'] = obj_data[alias_force_key] + alias_vals['alias_defaults'] = dict(((k, obj_data[v]) for k, v in alias_defaults.iteritems())) + alias_id = mail_alias.create_unique_alias(cr, SUPERUSER_ID, alias_vals, model_name=context.get('alias_model_name', child_model_name)) + child_class_model.write(cr, SUPERUSER_ID, obj_data['id'], {'alias_id': alias_id}) + _logger.info('Mail alias created for %s %s (uid %s)', child_model_name, obj_data[alias_key], obj_data['id']) + + try: + cr.execute('ALTER TABLE %s ALTER COLUMN alias_id SET NOT NULL' % child_table_name) + except Exception: + _logger.warning("Table '%s': unable to set a NOT NULL constraint on column '%s' !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL", child_table_name, 'alias_id', child_table_name, 'alias_id') + + alias_id_column.required = True + return + + def create_unique_alias(self, cr, uid, vals, model_name = None, context = None): + """Creates an email.alias record according to the values provided in ``vals``, + with 2 alterations: the ``alias_name`` value may be suffixed in order to + make it unique (and certain unsafe characters replaced), and + he ``alias_model_id`` value will set to the model ID of the ``model_name`` + value, if provided, + """ + alias_name = remove_accents(vals['alias_name']).lower().split('@')[0] + alias_name = re.sub('[^\\w+.]+', '-', alias_name) + alias_name = self._find_unique(cr, uid, alias_name, context=context) + vals['alias_name'] = alias_name + if model_name: + model_id = self.pool.get('ir.model').search(cr, uid, [('model', '=', model_name)], context=context)[0] + vals['alias_model_id'] = model_id + return self.create(cr, uid, vals, context=context) \ No newline at end of file diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 259bb86ca0ce5..436b83c6bd8df 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -1,184 +1,143 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2009-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see -# -############################################################################## -from email.utils import formataddr - -from openerp.osv import osv, fields -from openerp import tools, SUPERUSER_ID - -class mail_followers(osv.Model): - """ mail_followers holds the data related to the follow mechanism inside - OpenERP. Partners can choose to follow documents (records) of any kind - that inherits from mail.thread. Following documents allow to receive - notifications for new messages. - A subscription is characterized by: - :param: res_model: model of the followed objects - :param: res_id: ID of resource (may be 0 for every objects) - """ - _name = 'mail.followers' - _rec_name = 'partner_id' - _log_access = False - _description = 'Document Followers' - _columns = { - 'res_model': fields.char('Related Document Model', size=128, - required=True, select=1, - help='Model of the followed resource'), - 'res_id': fields.integer('Related Document ID', select=1, - help='Id of the followed resource'), - 'partner_id': fields.many2one('res.partner', string='Related Partner', - ondelete='cascade', required=True, select=1), - 'subtype_ids': fields.many2many('mail.message.subtype', string='Subtype', - help="Message subtypes followed, meaning subtypes that will be pushed onto the user's Wall."), - } - - -class mail_notification(osv.Model): - """ Class holding notifications pushed to partners. Followers and partners - added in 'contacts to notify' receive notifications. """ - _name = 'mail.notification' - _rec_name = 'partner_id' - _log_access = False - _description = 'Notifications' - - _columns = { - 'partner_id': fields.many2one('res.partner', string='Contact', - ondelete='cascade', required=True, select=1), - 'read': fields.boolean('Read', select=1), - 'starred': fields.boolean('Starred', select=1, - help='Starred message that goes into the todo mailbox'), - 'message_id': fields.many2one('mail.message', string='Message', - ondelete='cascade', required=True, select=1), - } - - _defaults = { - 'read': False, - 'starred': False, - } - - def init(self, cr): - cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',)) - if not cr.fetchone(): - cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)') - - def get_partners_to_notify(self, cr, uid, message, partners_to_notify=None, context=None): - """ Return the list of partners to notify, based on their preferences. - - :param browse_record message: mail.message to notify - :param list partners_to_notify: optional list of partner ids restricting - the notifications to process - """ - notify_pids = [] - for notification in message.notification_ids: - if notification.read: - continue - partner = notification.partner_id - # If partners_to_notify specified: restrict to them - if partners_to_notify is not None and partner.id not in partners_to_notify: - continue - # Do not send to partners without email address defined - if not partner.email: - continue - # Do not send to partners having same email address than the author (can cause loops or bounce effect due to messy database) - if message.author_id and message.author_id.email == partner.email: - continue - # Partner does not want to receive any emails or is opt-out - if partner.notification_email_send == 'none': - continue - # Partner wants to receive only emails and comments - if partner.notification_email_send == 'comment' and message.type not in ('email', 'comment'): - continue - # Partner wants to receive only emails - if partner.notification_email_send == 'email' and message.type != 'email': - continue - notify_pids.append(partner.id) - return notify_pids - - def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None): - """ Send by email the notification depending on the user preferences - - :param list partners_to_notify: optional list of partner ids restricting - the notifications to process - """ - if context is None: - context = {} - mail_message_obj = self.pool.get('mail.message') - - # optional list of partners to notify: subscribe them if not already done or update the notification - if partners_to_notify: - notifications_to_update = [] - notified_partners = [] - notif_ids = self.search(cr, SUPERUSER_ID, [('message_id', '=', msg_id), ('partner_id', 'in', partners_to_notify)], context=context) - for notification in self.browse(cr, SUPERUSER_ID, notif_ids, context=context): - notified_partners.append(notification.partner_id.id) - notifications_to_update.append(notification.id) - partners_to_notify = filter(lambda item: item not in notified_partners, partners_to_notify) - if notifications_to_update: - self.write(cr, SUPERUSER_ID, notifications_to_update, {'read': False}, context=context) - mail_message_obj.write(cr, uid, msg_id, {'notified_partner_ids': [(4, id) for id in partners_to_notify]}, context=context) - - # mail_notify_noemail (do not send email) or no partner_ids: do not send, return - if context.get('mail_notify_noemail'): - return True - # browse as SUPERUSER_ID because of access to res_partner not necessarily allowed - msg = self.pool.get('mail.message').browse(cr, SUPERUSER_ID, msg_id, context=context) - notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, partners_to_notify=partners_to_notify, context=context) - if not notify_partner_ids: - return True - - # add the context in the email - # TDE FIXME: commented, to be improved in a future branch - # quote_context = self.pool.get('mail.message').message_quote_context(cr, uid, msg_id, context=context) - - mail_mail = self.pool.get('mail.mail') - # add signature - body_html = msg.body - # if quote_context: - # body_html = tools.append_content_to_html(body_html, quote_context, plaintext=False) - signature = msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0].signature or '' - if signature: - body_html = tools.append_content_to_html(body_html, signature, plaintext=True, container_tag='div') - - # email_from: partner-user alias or partner email or mail.message email_from - if msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0].alias_domain and msg.author_id.user_ids[0].alias_name: - email_from = formataddr((msg.author_id.name, '%s@%s' % (msg.author_id.user_ids[0].alias_name, msg.author_id.user_ids[0].alias_domain))) - elif msg.author_id: - email_from = formataddr((msg.author_id.name, msg.author_id.email)) - else: - email_from = msg.email_from - - references = False - if msg.parent_id: - references = msg.parent_id.message_id - - mail_values = { - 'mail_message_id': msg.id, - 'auto_delete': True, - 'body_html': body_html, - 'email_from': email_from, - 'references': references, - } - if 'mail_server_id' in context: - # temporary workaround for mail from send mail wizard - mail_values['mail_server_id'] = context['mail_server_id'] - email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) - try: - return mail_mail.send(cr, uid, [email_notif_id], recipient_ids=notify_partner_ids, context=context) - except Exception: - return False +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import osv, fields +from openerp import tools, SUPERUSER_ID + +class mail_followers(osv.Model): + """ mail_followers holds the data related to the follow mechanism inside + OpenERP. Partners can choose to follow documents (records) of any kind + that inherits from mail.thread. Following documents allow to receive + notifications for new messages. + A subscription is characterized by: + :param: res_model: model of the followed objects + :param: res_id: ID of resource (may be 0 for every objects) + """ + _name = 'mail.followers' + _rec_name = 'partner_id' + _log_access = False + _description = 'Document Followers' + _columns = {'res_model': fields.char('Related Document Model', size=128, required=True, select=1, help='Model of the followed resource'), + 'res_id': fields.integer('Related Document ID', select=1, help='Id of the followed resource'), + 'partner_id': fields.many2one('res.partner', string='Related Partner', ondelete='cascade', required=True, select=1), + 'subtype_ids': fields.many2many('mail.message.subtype', string='Subtype', help="Message subtypes followed, meaning subtypes that will be pushed onto the user's Wall.")} + + +class mail_notification(osv.Model): + """ Class holding notifications pushed to partners. Followers and partners + added in 'contacts to notify' receive notifications. """ + _name = 'mail.notification' + _rec_name = 'partner_id' + _log_access = False + _description = 'Notifications' + _columns = {'partner_id': fields.many2one('res.partner', string='Contact', ondelete='cascade', required=True, select=1), + 'read': fields.boolean('Read', select=1), + 'starred': fields.boolean('Starred', select=1, help='Starred message that goes into the todo mailbox'), + 'message_id': fields.many2one('mail.message', string='Message', ondelete='cascade', required=True, select=1)} + _defaults = {'read': False, + 'starred': False} + + def init(self, cr): + cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',)) + if not cr.fetchone(): + cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)') + + def get_partners_to_notify(self, cr, uid, message, partners_to_notify = None, context = None): + """ Return the list of partners to notify, based on their preferences. + + :param browse_record message: mail.message to notify + :param list partners_to_notify: optional list of partner ids restricting + the notifications to process + """ + notify_pids = [] + for notification in message.notification_ids: + if notification.read: + continue + partner = notification.partner_id + if partners_to_notify is not None and partner.id not in partners_to_notify: + continue + if not partner.email: + continue + if message.author_id and message.author_id.email == partner.email: + continue + if partner.notification_email_send == 'none': + continue + if partner.notification_email_send == 'comment' and message.type not in ('email', 'comment'): + continue + if partner.notification_email_send == 'email' and message.type != 'email': + continue + notify_pids.append(partner.id) + + return notify_pids + + def _notify(self, cr, uid, msg_id, partners_to_notify = None, context = None): + """ Send by email the notification depending on the user preferences + + :param list partners_to_notify: optional list of partner ids restricting + the notifications to process + """ + if context is None: + context = {} + mail_message_obj = self.pool.get('mail.message') + if partners_to_notify: + notifications_to_update = [] + notified_partners = [] + notif_ids = self.search(cr, SUPERUSER_ID, [('message_id', '=', msg_id), ('partner_id', 'in', partners_to_notify)], context=context) + for notification in self.browse(cr, SUPERUSER_ID, notif_ids, context=context): + notified_partners.append(notification.partner_id.id) + notifications_to_update.append(notification.id) + + partners_to_notify = filter(lambda item: item not in notified_partners, partners_to_notify) + if notifications_to_update: + self.write(cr, SUPERUSER_ID, notifications_to_update, {'read': False}, context=context) + mail_message_obj.write(cr, uid, msg_id, {'notified_partner_ids': [ (4, id) for id in partners_to_notify ]}, context=context) + if context.get('mail_notify_noemail'): + return True + else: + msg = self.pool.get('mail.message').browse(cr, SUPERUSER_ID, msg_id, context=context) + notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, partners_to_notify=partners_to_notify, context=context) + if not notify_partner_ids: + return True + mail_mail = self.pool.get('mail.mail') + body_html = msg.body + signature = msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0].signature or '' + if signature: + body_html = tools.append_content_to_html(body_html, signature, plaintext=True, container_tag='div') + if msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0].alias_domain and msg.author_id.user_ids[0].alias_name: + email_from = '%s <%s@%s>' % (msg.author_id.name, msg.author_id.user_ids[0].alias_name, msg.author_id.user_ids[0].alias_domain) + elif msg.author_id: + email_from = '%s <%s>' % (msg.author_id.name, msg.author_id.email) + else: + email_from = msg.email_from + references = False + if msg.parent_id: + references = msg.parent_id.message_id + mail_values = {'mail_message_id': msg.id, + 'auto_delete': True, + 'body_html': body_html, + 'email_from': email_from, + 'references': references} + email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) + try: + return mail_mail.send(cr, uid, [email_notif_id], recipient_ids=notify_partner_ids, context=context) + except Exception: + return False + + return \ No newline at end of file diff --git a/addons/mail/mail_followers_view.xml b/addons/mail/mail_followers_view.xml index a9d1d80240b3b..4929c6c098463 100644 --- a/addons/mail/mail_followers_view.xml +++ b/addons/mail/mail_followers_view.xml @@ -8,7 +8,7 @@ mail.followers 10 - + @@ -20,7 +20,7 @@ mail.followers.form mail.followers -
    + @@ -51,7 +51,7 @@ - Followers + Theo dõi mail.followers form tree,form @@ -65,7 +65,7 @@ - --> diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 719dfad161b68..1d93a1760be16 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -1,216 +1,157 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see -# -############################################################################## - -import openerp -import openerp.tools as tools -from openerp.osv import osv -from openerp.osv import fields -from openerp import SUPERUSER_ID -from openerp.tools.translate import _ - -class mail_group(osv.Model): - """ A mail_group is a collection of users sharing messages in a discussion - group. The group mechanics are based on the followers. """ - _description = 'Discussion group' - _name = 'mail.group' - _mail_flat_thread = False - _inherit = ['mail.thread'] - _inherits = {'mail.alias': 'alias_id'} - - def _get_image(self, cr, uid, ids, name, args, context=None): - result = dict.fromkeys(ids, False) - for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = tools.image_get_resized_images(obj.image) - return result - - def _set_image(self, cr, uid, id, name, value, args, context=None): - return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) - - _columns = { - 'name': fields.char('Name', size=64, required=True, translate=True), - 'description': fields.text('Description'), - 'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"), - 'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')], 'Privacy', required=True, - help='This group is visible by non members. \ - Invisible groups can add members through the invite button.'), - 'group_public_id': fields.many2one('res.groups', string='Authorized Group'), - 'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel', - id1='mail_group_id', id2='groups_id', string='Auto Subscription', - help="Members of those groups will automatically added as followers. "\ - "Note that they will be able to manage their subscription manually "\ - "if necessary."), - # image: all image fields are base64 encoded and PIL-supported - 'image': fields.binary("Photo", - help="This field holds the image used as photo for the group, limited to 1024x1024px."), - 'image_medium': fields.function(_get_image, fnct_inv=_set_image, - string="Medium-sized photo", type="binary", multi="_get_image", - store={ - 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), - }, - help="Medium-sized photo of the group. It is automatically "\ - "resized as a 128x128px image, with aspect ratio preserved. "\ - "Use this field in form views or some kanban views."), - 'image_small': fields.function(_get_image, fnct_inv=_set_image, - string="Small-sized photo", type="binary", multi="_get_image", - store={ - 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), - }, - help="Small-sized photo of the group. It is automatically "\ - "resized as a 64x64px image, with aspect ratio preserved. "\ - "Use this field anywhere a small image is required."), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, - help="The email address associated with this group. New emails received will automatically " - "create new topics."), - } - - def _get_default_employee_group(self, cr, uid, context=None): - ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') - return ref and ref[1] or False - - def _get_default_image(self, cr, uid, context=None): - image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png') - return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) - - _defaults = { - 'public': 'groups', - 'group_public_id': _get_default_employee_group, - 'image': _get_default_image, - 'alias_domain': False, # always hide alias during creation - } - - def _generate_header_description(self, cr, uid, group, context=None): - header = '' - if group.description: - header = '%s' % group.description - if group.alias_id and group.alias_id.alias_name and group.alias_id.alias_domain: - if header: - header = '%s
    ' % header - return '%sGroup email gateway: %s@%s' % (header, group.alias_id.alias_name, group.alias_id.alias_domain) - return header - - def _subscribe_users(self, cr, uid, ids, context=None): - for mail_group in self.browse(cr, uid, ids, context=context): - partner_ids = [] - for group in mail_group.group_ids: - partner_ids += [user.partner_id.id for user in group.users] - self.message_subscribe(cr, uid, ids, partner_ids, context=context) - - def create(self, cr, uid, vals, context=None): - mail_alias = self.pool.get('mail.alias') - if not vals.get('alias_id'): - vals.pop('alias_name', None) # prevent errors during copy() - alias_id = mail_alias.create_unique_alias(cr, uid, - # Using '+' allows using subaddressing for those who don't - # have a catchall domain setup. - {'alias_name': "group+" + vals['name']}, - model_name=self._name, context=context) - vals['alias_id'] = alias_id - - # get parent menu - menu_parent = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root') - menu_parent = menu_parent and menu_parent[1] or False - - # Create menu id - mobj = self.pool.get('ir.ui.menu') - menu_id = mobj.create(cr, SUPERUSER_ID, {'name': vals['name'], 'parent_id': menu_parent}, context=context) - vals['menu_id'] = menu_id - - # Create group and alias - mail_group_id = super(mail_group, self).create(cr, uid, vals, context=context) - mail_alias.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context) - group = self.browse(cr, uid, mail_group_id, context=context) - - # Create client action for this group and link the menu to it - ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'action_mail_group_feeds') - if ref: - search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search') - params = { - 'search_view_id': search_ref and search_ref[1] or False, - 'domain': [ - ('model', '=', 'mail.group'), - ('res_id', '=', mail_group_id), - ], - 'context': { - 'default_model': 'mail.group', - 'default_res_id': mail_group_id, - }, - 'res_model': 'mail.message', - 'thread_level': 1, - 'header_description': self._generate_header_description(cr, uid, group, context=context), - 'view_mailbox': True, - 'compose_placeholder': 'Send a message to the group', - } - cobj = self.pool.get('ir.actions.client') - newref = cobj.copy(cr, SUPERUSER_ID, ref[1], default={'params': str(params), 'name': vals['name']}, context=context) - mobj.write(cr, SUPERUSER_ID, menu_id, {'action': 'ir.actions.client,' + str(newref), 'mail_group_id': mail_group_id}, context=context) - - if vals.get('group_ids'): - self._subscribe_users(cr, uid, [mail_group_id], context=context) - return mail_group_id - - def unlink(self, cr, uid, ids, context=None): - groups = self.browse(cr, uid, ids, context=context) - # Cascade-delete mail aliases as well, as they should not exist without the mail group. - mail_alias = self.pool.get('mail.alias') - alias_ids = [group.alias_id.id for group in groups if group.alias_id] - # Delete mail_group - try: - all_emp_group = self.pool['ir.model.data'].get_object_reference(cr, uid, 'mail', 'group_all_employees')[1] - except ValueError: - all_emp_group = None - if all_emp_group and all_emp_group in ids: - raise osv.except_osv(_('Warning!'), _('You cannot delete those groups, as the Whole Company group is required by other modules.')) - res = super(mail_group, self).unlink(cr, uid, ids, context=context) - # Delete alias - mail_alias.unlink(cr, SUPERUSER_ID, alias_ids, context=context) - # Cascade-delete menu entries as well - self.pool.get('ir.ui.menu').unlink(cr, SUPERUSER_ID, [group.menu_id.id for group in groups if group.menu_id], context=context) - return res - - def write(self, cr, uid, ids, vals, context=None): - result = super(mail_group, self).write(cr, uid, ids, vals, context=context) - if vals.get('group_ids'): - self._subscribe_users(cr, uid, ids, context=context) - # if description, name or alias is changed: update client action - if vals.get('description') or vals.get('name') or vals.get('alias_id') or vals.get('alias_name'): - cobj = self.pool.get('ir.actions.client') - for action in [group.menu_id.action for group in self.browse(cr, uid, ids, context=context)]: - new_params = action.params - new_params['header_description'] = self._generate_header_description(cr, uid, group, context=context) - cobj.write(cr, SUPERUSER_ID, [action.id], {'params': str(new_params)}, context=context) - # if name is changed: update menu - if vals.get('name'): - mobj = self.pool.get('ir.ui.menu') - mobj.write(cr, SUPERUSER_ID, - [group.menu_id.id for group in self.browse(cr, uid, ids, context=context)], - {'name': vals.get('name')}, context=context) - - return result - - def action_follow(self, cr, uid, ids, context=None): - """ Wrapper because message_subscribe_users take a user_ids=None - that receive the context without the wrapper. """ - return self.message_subscribe_users(cr, uid, ids, context=context) - - def action_unfollow(self, cr, uid, ids, context=None): - """ Wrapper because message_unsubscribe_users take a user_ids=None - that receive the context without the wrapper. """ - return self.message_unsubscribe_users(cr, uid, ids, context=context) +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import openerp +import openerp.tools as tools +from openerp.osv import osv +from openerp.osv import fields +from openerp import SUPERUSER_ID + +class mail_group(osv.Model): + """ A mail_group is a collection of users sharing messages in a discussion + group. The group mechanics are based on the followers. """ + _description = 'Discussion group' + _name = 'mail.group' + _mail_flat_thread = False + _inherit = ['mail.thread'] + _inherits = {'mail.alias': 'alias_id'} + + def _get_image(self, cr, uid, ids, name, args, context = None): + result = dict.fromkeys(ids, False) + for obj in self.browse(cr, uid, ids, context=context): + result[obj.id] = tools.image_get_resized_images(obj.image) + + return result + + def _set_image(self, cr, uid, id, name, value, args, context = None): + return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) + + _columns = {'name': fields.char('Name', size=64, required=True, translate=True), + 'description': fields.text('Description'), + 'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete='cascade'), + 'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')], 'Privacy', required=True, help='This group is visible by non members. Invisible groups can add members through the invite button.'), + 'group_public_id': fields.many2one('res.groups', string='Authorized Group'), + 'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel', id1='mail_group_id', id2='groups_id', string='Auto Subscription', help='Members of those groups will automatically added as followers. Note that they will be able to manage their subscription manually if necessary.'), + 'image': fields.binary('Photo', help='This field holds the image used as photo for the group, limited to 1024x1024px.'), + 'image_medium': fields.function(_get_image, fnct_inv=_set_image, string='Medium-sized photo', type='binary', multi='_get_image', store={'mail.group': (lambda self, cr, uid, ids, c = {}: ids, ['image'], 10)}, help='Medium-sized photo of the group. It is automatically resized as a 128x128px image, with aspect ratio preserved. Use this field in form views or some kanban views.'), + 'image_small': fields.function(_get_image, fnct_inv=_set_image, string='Small-sized photo', type='binary', multi='_get_image', store={'mail.group': (lambda self, cr, uid, ids, c = {}: ids, ['image'], 10)}, help='Small-sized photo of the group. It is automatically resized as a 64x64px image, with aspect ratio preserved. Use this field anywhere a small image is required.'), + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete='restrict', required=True, help='The email address associated with this group. New emails received will automatically create new topics.')} + + def _get_default_employee_group(self, cr, uid, context = None): + ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') + return ref and ref[1] or False + + def _get_default_image(self, cr, uid, context = None): + image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png') + return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) + + _defaults = {'public': 'groups', + 'group_public_id': _get_default_employee_group, + 'image': _get_default_image, + 'alias_domain': False} + + def _generate_header_description(self, cr, uid, group, context = None): + header = '' + if group.description: + header = '%s' % group.description + if group.alias_id and group.alias_id.alias_name and group.alias_id.alias_domain: + if header: + header = '%s
    ' % header + return '%sGroup email gateway: %s@%s' % (header, group.alias_id.alias_name, group.alias_id.alias_domain) + return header + + def _subscribe_users(self, cr, uid, ids, context = None): + for mail_group in self.browse(cr, uid, ids, context=context): + partner_ids = [] + for group in mail_group.group_ids: + partner_ids += [ user.partner_id.id for user in group.users ] + + self.message_subscribe(cr, uid, ids, partner_ids, context=context) + + def create(self, cr, uid, vals, context = None): + mail_alias = self.pool.get('mail.alias') + if not vals.get('alias_id'): + vals.pop('alias_name', None) + alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': 'group+' + vals['name']}, model_name=self._name, context=context) + vals['alias_id'] = alias_id + menu_parent = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root') + menu_parent = menu_parent and menu_parent[1] or False + mobj = self.pool.get('ir.ui.menu') + menu_id = mobj.create(cr, SUPERUSER_ID, {'name': vals['name'], + 'parent_id': menu_parent}, context=context) + vals['menu_id'] = menu_id + mail_group_id = super(mail_group, self).create(cr, uid, vals, context=context) + mail_alias.write(cr, uid, [vals['alias_id']], {'alias_force_thread_id': mail_group_id}, context) + group = self.browse(cr, uid, mail_group_id, context=context) + ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'action_mail_group_feeds') + if ref: + search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search') + params = {'search_view_id': search_ref and search_ref[1] or False, + 'domain': [('model', '=', 'mail.group'), ('res_id', '=', mail_group_id)], + 'context': {'default_model': 'mail.group', + 'default_res_id': mail_group_id}, + 'res_model': 'mail.message', + 'thread_level': 1, + 'header_description': self._generate_header_description(cr, uid, group, context=context), + 'view_mailbox': True, + 'compose_placeholder': 'Send a message to the group'} + cobj = self.pool.get('ir.actions.client') + newref = cobj.copy(cr, SUPERUSER_ID, ref[1], default={'params': str(params), + 'name': vals['name']}, context=context) + mobj.write(cr, SUPERUSER_ID, menu_id, {'action': 'ir.actions.client,' + str(newref), + 'mail_group_id': mail_group_id}, context=context) + if vals.get('group_ids'): + self._subscribe_users(cr, uid, [mail_group_id], context=context) + return mail_group_id + + def unlink(self, cr, uid, ids, context = None): + groups = self.browse(cr, uid, ids, context=context) + mail_alias = self.pool.get('mail.alias') + alias_ids = [ group.alias_id.id for group in groups if group.alias_id ] + res = super(mail_group, self).unlink(cr, uid, ids, context=context) + mail_alias.unlink(cr, SUPERUSER_ID, alias_ids, context=context) + self.pool.get('ir.ui.menu').unlink(cr, SUPERUSER_ID, [ group.menu_id.id for group in groups if group.menu_id ], context=context) + return res + + def write(self, cr, uid, ids, vals, context = None): + result = super(mail_group, self).write(cr, uid, ids, vals, context=context) + if vals.get('group_ids'): + self._subscribe_users(cr, uid, ids, context=context) + if vals.get('description') or vals.get('name') or vals.get('alias_id') or vals.get('alias_name'): + cobj = self.pool.get('ir.actions.client') + for action in [ group.menu_id.action for group in self.browse(cr, uid, ids, context=context) ]: + new_params = action.params + new_params['header_description'] = self._generate_header_description(cr, uid, group, context=context) + cobj.write(cr, SUPERUSER_ID, [action.id], {'params': str(new_params)}, context=context) + + if vals.get('name'): + mobj = self.pool.get('ir.ui.menu') + mobj.write(cr, SUPERUSER_ID, [ group.menu_id.id for group in self.browse(cr, uid, ids, context=context) ], {'name': vals.get('name')}, context=context) + return result + + def action_follow(self, cr, uid, ids, context = None): + """ Wrapper because message_subscribe_users take a user_ids=None + that receive the context without the wrapper. """ + return self.message_subscribe_users(cr, uid, ids, context=context) + + def action_unfollow(self, cr, uid, ids, context = None): + """ Wrapper because message_unsubscribe_users take a user_ids=None + that receive the context without the wrapper. """ + return self.message_unsubscribe_users(cr, uid, ids, context=context) \ No newline at end of file diff --git a/addons/mail/mail_group_menu.py b/addons/mail/mail_group_menu.py index 56d4b5dd5648f..08a506b239df0 100644 --- a/addons/mail/mail_group_menu.py +++ b/addons/mail/mail_group_menu.py @@ -1,58 +1,50 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2012-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see -# -############################################################################## - -from openerp import SUPERUSER_ID -from openerp.osv import osv -from openerp.osv import fields - - -class ir_ui_menu(osv.osv): - """ Override of ir.ui.menu class. When adding mail_thread module, each - new mail.group will create a menu entry. This overrides checks that - the current user is in the mail.group followers. If not, the menu - entry is taken off the list of menu ids. This way the user will see - menu entries for the mail.group he is following. - """ - _inherit = 'ir.ui.menu' - - _columns = { - 'mail_group_id': fields.many2one('mail.group', 'Mail Group') - } - - def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): - """ Override to take off menu entries (mail.group) the user is not - following. Access are done using SUPERUSER_ID to avoid access - rights issues for an internal back-end algorithm. """ - ids = super(ir_ui_menu, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=False) - partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - follower_obj = self.pool.get('mail.followers') - for menu in self.browse(cr, uid, ids, context=context): - if menu.mail_group_id: - sub_ids = follower_obj.search(cr, SUPERUSER_ID, [ - ('partner_id', '=', partner_id), - ('res_model', '=', 'mail.group'), - ('res_id', '=', menu.mail_group_id.id) - ], context=context) - if not sub_ids: - ids.remove(menu.id) - if count: - return len(ids) - return ids +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import SUPERUSER_ID +from openerp.osv import osv +from openerp.osv import fields + +class ir_ui_menu(osv.osv): + """ Override of ir.ui.menu class. When adding mail_thread module, each + new mail.group will create a menu entry. This overrides checks that + the current user is in the mail.group followers. If not, the menu + entry is taken off the list of menu ids. This way the user will see + menu entries for the mail.group he is following. + """ + _inherit = 'ir.ui.menu' + _columns = {'mail_group_id': fields.many2one('mail.group', 'Mail Group')} + + def search(self, cr, uid, args, offset = 0, limit = None, order = None, context = None, count = False): + """ Override to take off menu entries (mail.group) the user is not + following. Access are done using SUPERUSER_ID to avoid access + rights issues for an internal back-end algorithm. """ + ids = super(ir_ui_menu, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=False) + partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + follower_obj = self.pool.get('mail.followers') + for menu in self.browse(cr, uid, ids, context=context): + if menu.mail_group_id: + sub_ids = follower_obj.search(cr, SUPERUSER_ID, [('partner_id', '=', partner_id), ('res_model', '=', 'mail.group'), ('res_id', '=', menu.mail_group_id.id)], context=context) + if not sub_ids: + ids.remove(menu.id) + + if count: + return len(ids) + return ids \ No newline at end of file diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 5ccee40d5c7ea..20488dfb7c37a 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -13,7 +13,7 @@ }

    - No message in this group. + Không có thông điệp nào cho nhóm này.

    @@ -66,12 +66,12 @@ mail.group - +
    -

    @@ -105,7 +105,7 @@
    - +
    @@ -117,8 +117,8 @@ mail.group - - + + @@ -129,7 +129,7 @@ mail.group - + @@ -137,7 +137,7 @@ - Join a group + Tham gia một nhóm mail.group form kanban,tree,form @@ -145,7 +145,7 @@ - + diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index aa02654ef58ce..d2744bac90ad1 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -1,355 +1,270 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see -# -############################################################################## - -import base64 -import logging -import re -from email.utils import formataddr -from urllib import urlencode -from urlparse import urljoin - -import psycopg2 - -from openerp import tools -from openerp import SUPERUSER_ID -from openerp.osv import fields, osv -from openerp.osv.orm import except_orm -from openerp.tools.translate import _ - -_logger = logging.getLogger(__name__) - - -class mail_mail(osv.Model): - """ Model holding RFC2822 email messages to send. This model also provides - facilities to queue and send new email messages. """ - _name = 'mail.mail' - _description = 'Outgoing Mails' - _inherits = {'mail.message': 'mail_message_id'} - _order = 'id desc' - - _columns = { - 'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), - 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), - 'state': fields.selection([ - ('outgoing', 'Outgoing'), - ('sent', 'Sent'), - ('received', 'Received'), - ('exception', 'Delivery Failed'), - ('cancel', 'Cancelled'), - ], 'Status', readonly=True), - 'auto_delete': fields.boolean('Auto Delete', - help="Permanently delete this email after sending it, to save space"), - 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), - 'email_from': fields.char('From', help='Message sender, taken from user preferences.'), - 'email_to': fields.text('To', help='Message recipients'), - 'email_cc': fields.char('Cc', help='Carbon copy message recipients'), - 'reply_to': fields.char('Reply-To', help='Preferred response address for the message'), - 'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"), - - # Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification - # and during unlink() we will not cascade delete the parent and its attachments - 'notification': fields.boolean('Is Notification') - } - - def _get_default_from(self, cr, uid, context=None): - this = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if this.alias_domain: - return '%s@%s' % (this.alias_name, this.alias_domain) - elif this.email: - return this.email - raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) - - _defaults = { - 'state': 'outgoing', - 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx), - } - - def default_get(self, cr, uid, fields, context=None): - # protection for `default_type` values leaking from menu action context (e.g. for invoices) - # To remove when automatic context propagation is removed in web client - if context and context.get('default_type') and context.get('default_type') not in self._all_columns['type'].column.selection: - context = dict(context, default_type=None) - return super(mail_mail, self).default_get(cr, uid, fields, context=context) - - def create(self, cr, uid, values, context=None): - if 'notification' not in values and values.get('mail_message_id'): - values['notification'] = True - return super(mail_mail, self).create(cr, uid, values, context=context) - - def unlink(self, cr, uid, ids, context=None): - # cascade-delete the parent message for all mails that are not created for a notification - ids_to_cascade = self.search(cr, uid, [('notification', '=', False), ('id', 'in', ids)]) - parent_msg_ids = [m.mail_message_id.id for m in self.browse(cr, uid, ids_to_cascade, context=context)] - res = super(mail_mail, self).unlink(cr, uid, ids, context=context) - self.pool.get('mail.message').unlink(cr, uid, parent_msg_ids, context=context) - return res - - def mark_outgoing(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'state': 'outgoing'}, context=context) - - def cancel(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'state': 'cancel'}, context=context) - - def process_email_queue(self, cr, uid, ids=None, context=None): - """Send immediately queued messages, committing after each - message is sent - this is not transactional and should - not be called during another transaction! - - :param list ids: optional list of emails ids to send. If passed - no search is performed, and these ids are used - instead. - :param dict context: if a 'filters' key is present in context, - this value will be used as an additional - filter to further restrict the outgoing - messages to send (by default all 'outgoing' - messages are sent). - """ - if context is None: - context = {} - if not ids: - filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')] - if 'filters' in context: - filters.extend(context['filters']) - ids = self.search(cr, uid, filters, context=context) - res = None - try: - # Force auto-commit - this is meant to be called by - # the scheduler, and we can't allow rolling back the status - # of previously sent emails! - res = self.send(cr, uid, ids, auto_commit=True, context=context) - except Exception: - _logger.exception("Failed processing mail queue") - return res - - def _postprocess_sent_message(self, cr, uid, mail, context=None): - """Perform any post-processing necessary after sending ``mail`` - successfully, including deleting it completely along with its - attachment if the ``auto_delete`` flag of the mail was set. - Overridden by subclasses for extra post-processing behaviors. - - :param browse_record mail: the mail that was just sent - :return: True - """ - if mail.auto_delete: - # done with SUPERUSER_ID to avoid giving large unlink access rights - self.unlink(cr, SUPERUSER_ID, [mail.id], context=context) - return True - - def send_get_mail_subject(self, cr, uid, mail, force=False, partner=None, context=None): - """ If subject is void and record_name defined: ' posted on ' - - :param boolean force: force the subject replacement - :param browse_record mail: mail.mail browse_record - :param browse_record partner: specific recipient partner - """ - if (force or not mail.subject) and mail.record_name: - return 'Re: %s' % (mail.record_name) - elif (force or not mail.subject) and mail.parent_id and mail.parent_id.subject: - return 'Re: %s' % (mail.parent_id.subject) - return mail.subject - - def send_get_mail_body(self, cr, uid, mail, partner=None, context=None): - """ Return a specific ir_email body. The main purpose of this method - is to be inherited by Portal, to add a link for signing in, in - each notification email a partner receives. - - :param browse_record mail: mail.mail browse_record - :param browse_record partner: specific recipient partner - """ - body = mail.body_html - # partner is a user, link to a related document (incentive to install portal) - if partner and partner.user_ids and mail.model and mail.res_id \ - and self.check_access_rights(cr, partner.user_ids[0].id, 'read', raise_exception=False): - related_user = partner.user_ids[0] - try: - self.pool.get(mail.model).check_access_rule(cr, related_user.id, [mail.res_id], 'read', context=context) - except except_orm, e: - pass - else: - base_url = self.pool.get('ir.config_parameter').get_param(cr, SUPERUSER_ID, 'web.base.url') - # the parameters to encode for the query and fragment part of url - query = {'db': cr.dbname} - fragment = { - 'login': related_user.login, - 'model': mail.model, - 'id': mail.res_id, - } - url = urljoin(base_url, "?%s#%s" % (urlencode(query), urlencode(fragment))) - text = _("""

    Access this document directly in OpenERP

    """) % url - body = tools.append_content_to_html(body, ("

    %s

    " % text), plaintext=False) - return body - - def send_get_mail_reply_to(self, cr, uid, mail, partner=None, context=None): - """ Return a specific ir_email reply_to. - - :param browse_record mail: mail.mail browse_record - :param browse_record partner: specific recipient partner - """ - if mail.reply_to: - return mail.reply_to - email_reply_to = False - - # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias - if mail.model and mail.res_id and hasattr(self.pool.get(mail.model), 'message_get_reply_to'): - email_reply_to = self.pool.get(mail.model).message_get_reply_to(cr, uid, [mail.res_id], context=context)[0] - # no alias reply_to -> reply_to will be the email_from, only the email part - if not email_reply_to and mail.email_from: - emails = tools.email_split(mail.email_from) - if emails: - email_reply_to = emails[0] - - # format 'Document name ' - if email_reply_to and mail.model and mail.res_id: - document_name = self.pool.get(mail.model).name_get(cr, SUPERUSER_ID, [mail.res_id], context=context)[0] - if document_name: - # generate reply to - - email_reply_to = formataddr((_('Followers of %s') % document_name[1], email_reply_to)) - - return email_reply_to - - def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): - """ Return a dictionary for specific email values, depending on a - partner, or generic to the whole recipients given by mail.email_to. - - :param browse_record mail: mail.mail browse_record - :param browse_record partner: specific recipient partner - """ - body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) - subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) - reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context) - body_alternative = tools.html2plaintext(body) - - # generate email_to, heuristic: - # 1. if 'partner' is specified and there is a related document: Followers of 'Doc' - # 2. if 'partner' is specified, but no related document: Partner Name - # 3; fallback on mail.email_to that we split to have an email addresses list - if partner and mail.record_name: - email_to = [formataddr((_('Followers of %s') % mail.record_name, partner.email))] - elif partner: - email_to = [formataddr((partner.name, partner.email))] - else: - email_to = tools.email_split(mail.email_to) - - return { - 'body': body, - 'body_alternative': body_alternative, - 'subject': subject, - 'email_to': email_to, - 'reply_to': reply_to, - } - - def send(self, cr, uid, ids, auto_commit=False, recipient_ids=None, context=None): - """ Sends the selected emails immediately, ignoring their current - state (mails that have already been sent should not be passed - unless they should actually be re-sent). - Emails successfully delivered are marked as 'sent', and those - that fail to be deliver are marked as 'exception', and the - corresponding error mail is output in the server logs. - - :param bool auto_commit: whether to force a commit of the mail status - after sending each mail (meant only for scheduler processing); - should never be True during normal transactions (default: False) - :param list recipient_ids: specific list of res.partner recipients. - If set, one email is sent to each partner. Its is possible to - tune the sent email through ``send_get_mail_body`` and ``send_get_mail_subject``. - If not specified, one email is sent to mail_mail.email_to. - :return: True - """ - ir_mail_server = self.pool.get('ir.mail_server') - for mail in self.browse(cr, uid, ids, context=context): - try: - # handle attachments - attachments = [] - for attach in mail.attachment_ids: - attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) - # specific behavior to customize the send email for notified partners - email_list = [] - if recipient_ids: - partner_obj = self.pool.get('res.partner') - existing_recipient_ids = partner_obj.exists(cr, SUPERUSER_ID, recipient_ids, context=context) - for partner in partner_obj.browse(cr, SUPERUSER_ID, existing_recipient_ids, context=context): - email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) - else: - email_list.append(self.send_get_email_dict(cr, uid, mail, context=context)) - - # build an RFC2822 email.message.Message object and send it without queuing - res = None - for email in email_list: - msg = ir_mail_server.build_email( - email_from = mail.email_from, - email_to = email.get('email_to'), - subject = email.get('subject'), - body = email.get('body'), - body_alternative = email.get('body_alternative'), - email_cc = tools.email_split(mail.email_cc), - reply_to = email.get('reply_to'), - attachments = attachments, - message_id = mail.message_id, - references = mail.references, - object_id = mail.res_id and ('%s-%s' % (mail.res_id, mail.model)), - subtype = 'html', - subtype_alternative = 'plain') - try: - res = ir_mail_server.send_email( - cr, uid, - msg, - mail_server_id=mail.mail_server_id.id, - context=context - ) - except AssertionError as error: - if error.message == ir_mail_server.NO_VALID_RECIPIENT: - # No valid recipient found for this particular - # mail item -> ignore error to avoid blocking - # delivery to next recipients, if any. If this is - # the only recipient, the mail will show as failed. - _logger.warning("Ignoring invalid recipients for mail.mail %s: %s", - mail.message_id, email.get('email_to')) - else: - raise - if res: - mail.write({'state': 'sent', 'message_id': res}) - mail_sent = True - else: - mail.write({'state': 'exception'}) - mail_sent = False - - # /!\ can't use mail.state here, as mail.refresh() will cause an error - # see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1 - if mail_sent: - self._postprocess_sent_message(cr, uid, mail, context=context) - except MemoryError: - # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job - # instead of marking the mail as failed - raise - except psycopg2.Error: - # If an error with the database occurs, chances are that the cursor is unusable. - # This will lead to an `psycopg2.InternalError` being raised when trying to write - # `state`, shadowing the original exception and forbid a retry on concurrent - # update. Let's bubble it. - raise - except Exception: - _logger.exception('failed sending mail.mail %s', mail.id) - mail.write({'state': 'exception'}) - - if auto_commit == True: - cr.commit() - return True +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import base64 +import logging +import re +from urllib import urlencode +from urlparse import urljoin +from openerp import tools +from openerp import SUPERUSER_ID +from openerp.osv import fields, osv +from openerp.osv.orm import except_orm +from openerp.tools.translate import _ +_logger = logging.getLogger(__name__) + +class mail_mail(osv.Model): + """ Model holding RFC2822 email messages to send. This model also provides + facilities to queue and send new email messages. """ + _name = 'mail.mail' + _description = 'Outgoing Mails' + _inherits = {'mail.message': 'mail_message_id'} + _order = 'id desc' + _columns = {'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), + 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), + 'state': fields.selection([('outgoing', 'Outgoing'), + ('sent', 'Sent'), + ('received', 'Received'), + ('exception', 'Delivery Failed'), + ('cancel', 'Cancelled')], 'Status', readonly=True), + 'auto_delete': fields.boolean('Auto Delete', help='Permanently delete this email after sending it, to save space'), + 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), + 'email_from': fields.char('From', help='Message sender, taken from user preferences.'), + 'email_to': fields.text('To', help='Message recipients'), + 'email_cc': fields.char('Cc', help='Carbon copy message recipients'), + 'reply_to': fields.char('Reply-To', help='Preferred response address for the message'), + 'body_html': fields.text('Rich-text Contents', help='Rich-text/HTML message'), + 'notification': fields.boolean('Is Notification')} + + def _get_default_from(self, cr, uid, context = None): + this = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if this.alias_domain: + return '%s@%s' % (this.alias_name, this.alias_domain) + if this.email: + return this.email + raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) + + _defaults = {'state': 'outgoing', + 'email_from': lambda self, cr, uid, ctx = None: self._get_default_from(cr, uid, ctx)} + + def default_get(self, cr, uid, fields, context = None): + if context and context.get('default_type') and context.get('default_type') not in self._all_columns['type'].column.selection: + context = dict(context, default_type=None) + return super(mail_mail, self).default_get(cr, uid, fields, context=context) + + def create(self, cr, uid, values, context = None): + if 'notification' not in values and values.get('mail_message_id'): + values['notification'] = True + return super(mail_mail, self).create(cr, uid, values, context=context) + + def unlink(self, cr, uid, ids, context = None): + ids_to_cascade = self.search(cr, uid, [('notification', '=', False), ('id', 'in', ids)]) + parent_msg_ids = [ m.mail_message_id.id for m in self.browse(cr, uid, ids_to_cascade, context=context) ] + res = super(mail_mail, self).unlink(cr, uid, ids, context=context) + self.pool.get('mail.message').unlink(cr, uid, parent_msg_ids, context=context) + return res + + def mark_outgoing(self, cr, uid, ids, context = None): + return self.write(cr, uid, ids, {'state': 'outgoing'}, context=context) + + def cancel(self, cr, uid, ids, context = None): + return self.write(cr, uid, ids, {'state': 'cancel'}, context=context) + + def process_email_queue(self, cr, uid, ids = None, context = None): + """Send immediately queued messages, committing after each + message is sent - this is not transactional and should + not be called during another transaction! + + :param list ids: optional list of emails ids to send. If passed + no search is performed, and these ids are used + instead. + :param dict context: if a 'filters' key is present in context, + this value will be used as an additional + filter to further restrict the outgoing + messages to send (by default all 'outgoing' + messages are sent). + """ + if context is None: + context = {} + if not ids: + filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')] + if 'filters' in context: + filters.extend(context['filters']) + ids = self.search(cr, uid, filters, context=context) + res = None + try: + res = self.send(cr, uid, ids, auto_commit=True, context=context) + except Exception: + _logger.exception('Failed processing mail queue') + + return res + + def _postprocess_sent_message(self, cr, uid, mail, context = None): + """Perform any post-processing necessary after sending ``mail`` + successfully, including deleting it completely along with its + attachment if the ``auto_delete`` flag of the mail was set. + Overridden by subclasses for extra post-processing behaviors. + + :param browse_record mail: the mail that was just sent + :return: True + """ + if mail.auto_delete: + self.unlink(cr, SUPERUSER_ID, [mail.id], context=context) + return True + + def send_get_mail_subject(self, cr, uid, mail, force = False, partner = None, context = None): + """ If subject is void and record_name defined: ' posted on ' + + :param boolean force: force the subject replacement + :param browse_record mail: mail.mail browse_record + :param browse_record partner: specific recipient partner + """ + if (force or not mail.subject) and mail.record_name: + return 'Re: %s' % mail.record_name + if (force or not mail.subject) and mail.parent_id and mail.parent_id.subject: + return 'Re: %s' % mail.parent_id.subject + return mail.subject + + def send_get_mail_body(self, cr, uid, mail, partner = None, context = None): + """ Return a specific ir_email body. The main purpose of this method + is to be inherited by Portal, to add a link for signing in, in + each notification email a partner receives. + + :param browse_record mail: mail.mail browse_record + :param browse_record partner: specific recipient partner + """ + body = mail.body_html + if partner and partner.user_ids and mail.model and mail.res_id and self.check_access_rights(cr, partner.user_ids[0].id, 'read', raise_exception=False): + related_user = partner.user_ids[0] + try: + self.pool.get(mail.model).check_access_rule(cr, related_user.id, [mail.res_id], 'read', context=context) + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + query = {'db': cr.dbname} + fragment = {'login': related_user.login, + 'model': mail.model, + 'id': mail.res_id} + url = urljoin(base_url, '?%s#%s' % (urlencode(query), urlencode(fragment))) + text = _('

    Access this document directly in OpenERP

    ') % url + body = tools.append_content_to_html(body, '

    %s

    ' % text, plaintext=False) + except except_orm as e: + pass + + return body + + def send_get_mail_reply_to(self, cr, uid, mail, partner = None, context = None): + """ Return a specific ir_email reply_to. + + :param browse_record mail: mail.mail browse_record + :param browse_record partner: specific recipient partner + """ + if mail.reply_to: + return mail.reply_to + email_reply_to = False + if mail.model and mail.res_id and hasattr(self.pool.get(mail.model), 'message_get_reply_to'): + email_reply_to = self.pool.get(mail.model).message_get_reply_to(cr, uid, [mail.res_id], context=context)[0] + if not email_reply_to and mail.email_from: + emails = tools.email_split(mail.email_from) + if emails: + email_reply_to = emails[0] + if email_reply_to and mail.model and mail.res_id: + document_name = self.pool.get(mail.model).name_get(cr, SUPERUSER_ID, [mail.res_id], context=context)[0] + if document_name: + sanitized_doc_name = re.sub('[^\\w+.]+', '-', document_name[1]) + email_reply_to = _('"Followers of %s" <%s>') % (sanitized_doc_name, email_reply_to) + return email_reply_to + + def send_get_email_dict(self, cr, uid, mail, partner = None, context = None): + """ Return a dictionary for specific email values, depending on a + partner, or generic to the whole recipients given by mail.email_to. + + :param browse_record mail: mail.mail browse_record + :param browse_record partner: specific recipient partner + """ + body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) + subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) + reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context) + body_alternative = tools.html2plaintext(body) + if partner and mail.record_name: + sanitized_record_name = re.sub('[^\\w+.]+', '-', mail.record_name) + email_to = [_('"Followers of %s" <%s>') % (sanitized_record_name, partner.email)] + elif partner: + email_to = ['%s <%s>' % (partner.name, partner.email)] + else: + email_to = tools.email_split(mail.email_to) + return {'body': body, + 'body_alternative': body_alternative, + 'subject': subject, + 'email_to': email_to, + 'reply_to': reply_to} + + def send(self, cr, uid, ids, auto_commit = False, recipient_ids = None, context = None): + """ Sends the selected emails immediately, ignoring their current + state (mails that have already been sent should not be passed + unless they should actually be re-sent). + Emails successfully delivered are marked as 'sent', and those + that fail to be deliver are marked as 'exception', and the + corresponding error mail is output in the server logs. + + :param bool auto_commit: whether to force a commit of the mail status + after sending each mail (meant only for scheduler processing); + should never be True during normal transactions (default: False) + :param list recipient_ids: specific list of res.partner recipients. + If set, one email is sent to each partner. Its is possible to + tune the sent email through ``send_get_mail_body`` and ``send_get_mail_subject``. + If not specified, one email is sent to mail_mail.email_to. + :return: True + """ + ir_mail_server = self.pool.get('ir.mail_server') + for mail in self.browse(cr, uid, ids, context=context): + try: + attachments = [] + for attach in mail.attachment_ids: + attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) + + email_list = [] + if recipient_ids: + for partner in self.pool.get('res.partner').browse(cr, SUPERUSER_ID, recipient_ids, context=context): + email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) + + else: + email_list.append(self.send_get_email_dict(cr, uid, mail, context=context)) + res = None + msg = ir_mail_server.build_email(email_from=mail.email_from, email_to=email.get('email_to'), subject=email.get('subject'), body=email.get('body'), body_alternative=email.get('body_alternative'), email_cc=tools.email_split(mail.email_cc), reply_to=email.get('reply_to'), attachments=attachments, message_id=mail.message_id, references=mail.references, object_id=mail.res_id and '%s-%s' % (mail.res_id, mail.model), subtype='html', subtype_alternative='plain') + for email in email_list: + msg = ir_mail_server.build_email(email_from=mail.email_from, email_to=email.get('email_to'), subject=email.get('subject'), body=email.get('body'), body_alternative=email.get('body_alternative'), email_cc=tools.email_split(mail.email_cc), reply_to=email.get('reply_to'), attachments=attachments, message_id=mail.message_id, references=mail.references, object_id=mail.res_id and '%s-%s' % (mail.res_id, mail.model), subtype='html', subtype_alternative='plain') + res = ir_mail_server.send_email(cr, uid, msg, mail_server_id=mail.mail_server_id.id, context=context) + + if res: + mail.write({'state': 'sent', + 'message_id': res}) + mail_sent = True + else: + mail.write({'state': 'exception'}) + mail_sent = False + if mail_sent: + self._postprocess_sent_message(cr, uid, mail, context=context) + except Exception: + _logger.exception('failed sending mail.mail %s', mail.id) + mail.write({'state': 'exception'}) + + if auto_commit == True: + cr.commit() + + return True \ No newline at end of file diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml index 361959a992789..fb2b66adb3609 100644 --- a/addons/mail/mail_mail_view.xml +++ b/addons/mail/mail_mail_view.xml @@ -7,13 +7,12 @@
    -
    @@ -69,7 +68,7 @@ - Attach a File + Đính kèm tập tin /web/binary/upload_attachment @@ -93,9 +92,9 @@
    -
    [
    +
    [
    - uploading + Đang cập nhật
    @@ -105,9 +104,9 @@
    -
    [
    +
    [
    - uploading + Đang cập nhật
    @@ -124,20 +123,20 @@ Followers of - this document + Tài liệu này - and + V - - + + - , others... + , Khác... <<< @@ -145,14 +144,14 @@
    - Attach a note that will not be sent to the followers + Đính kèm 1 lưu ý mà nó sẽ không gửi cho người theo dõi
    @@ -172,7 +171,7 @@

    - +

    @@ -187,11 +186,11 @@ - or - Write to my followers + Hoặc + Viết cho những người theo dõi của tôi @@ -204,14 +203,14 @@ display message on the wall when there are no message --> -
    No messages.
    +
    Không có thông điệp.
    - Uploading error - Please, wait while the file is uploading. + Cập nhật bị lỗi + Vui lòng, đợi trong khi tập tin đang cập nhật. - + 8 - like - unlike + Thích + Không thích diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml index 2315f210a7185..fe1b9f5304c33 100644 --- a/addons/mail/static/src/xml/mail_followers.xml +++ b/addons/mail/static/src/xml/mail_followers.xml @@ -8,9 +8,9 @@
    @@ -19,8 +19,8 @@

    -

    Followers

    - Add others +

    Các người theo dõi

    + Thêm người khác
    @@ -43,13 +43,13 @@ - +
    -
    And more.
    +
    Nhiều hơn.
    diff --git a/addons/mail/update.py b/addons/mail/update.py index 97a14747a2449..9c19a42684707 100644 --- a/addons/mail/update.py +++ b/addons/mail/update.py @@ -1,117 +1,117 @@ -# -*- coding: utf-8 -*- -import datetime -import logging -import sys -import urllib -import urllib2 - -from openerp import pooler, SUPERUSER_ID -from openerp import release -from openerp.osv import osv -from openerp.tools.translate import _ -from openerp.tools.safe_eval import safe_eval -from openerp.tools.config import config -from openerp.tools import misc - -_logger = logging.getLogger(__name__) - -""" -Time interval that will be used to determine up to which date we will -check the logs to see if a message we just received was already logged. -@type: datetime.timedelta -""" -_PREVIOUS_LOG_CHECK = datetime.timedelta(days=365) - -def get_sys_logs(self, cr, uid): - """ - Utility method to send a publisher warranty get logs messages. - """ - pool = pooler.get_pool(cr.dbname) - - dbuuid = pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') - db_create_date = pool.get('ir.config_parameter').get_param(cr, uid, 'database.create_date') - limit_date = datetime.datetime.now() - limit_date = limit_date - datetime.timedelta(15) - limit_date_str = limit_date.strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT) - nbr_users = pool.get("res.users").search(cr, uid, [], count=True) - nbr_active_users = pool.get("res.users").search(cr, uid, [("login_date", ">=", limit_date_str)], count=True) - nbr_share_users = False - nbr_active_share_users = False - if "share" in pool.get("res.users")._all_columns: - nbr_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True)], count=True) - nbr_active_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True), ("login_date", ">=", limit_date_str)], count=True) - user = pool.get("res.users").browse(cr, uid, uid) - - web_base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', 'False') - msg = { - "dbuuid": dbuuid, - "nbr_users": nbr_users, - "nbr_active_users": nbr_active_users, - "nbr_share_users": nbr_share_users, - "nbr_active_share_users": nbr_active_share_users, - "dbname": cr.dbname, - "db_create_date": db_create_date, - "version": release.version, - "language": user.lang, - "web_base_url": web_base_url, - } - msg.update(pool.get("res.company").read(cr,uid,[1],["name","email","phone"])[0]) - - add_arg = {"timeout":30} if sys.version_info >= (2,6) else {} - arguments = {'arg0': msg, "action": "update",} - arguments_raw = urllib.urlencode(arguments) - - url = config.get("publisher_warranty_url") - - uo = urllib2.urlopen(url, arguments_raw, **add_arg) - result = {} - try: - submit_result = uo.read() - result = safe_eval(submit_result) - finally: - uo.close() - return result - -class publisher_warranty_contract(osv.osv): - _name = "publisher_warranty.contract" - - def update_notification(self, cr, uid, ids, cron_mode=True, context=None): - """ - Send a message to OpenERP's publisher warranty server to check the - validity of the contracts, get notifications, etc... - - @param cron_mode: If true, catch all exceptions (appropriate for usage in a cron). - @type cron_mode: boolean - """ - try: - try: - result = get_sys_logs(self, cr, uid) - except Exception: - if cron_mode: # we don't want to see any stack trace in cron - return False - _logger.debug("Exception while sending a get logs messages", exc_info=1) - raise osv.except_osv(_("Error"), _("Error during communication with the publisher warranty server.")) - # old behavior based on res.log; now on mail.message, that is not necessarily installed - IMD = self.pool['ir.model.data'] - user = self.pool['res.users'].browse(cr, SUPERUSER_ID, SUPERUSER_ID) - try: - poster = IMD.get_object(cr, SUPERUSER_ID, 'mail', 'group_all_employees') - except ValueError: - # Cannot found group, post the message on the wall of the admin - poster = user - if not poster.exists(): - return True - for message in result["messages"]: - try: - poster.message_post(body=message, subtype='mt_comment', partner_ids=[user.partner_id.id]) - except Exception: - _logger.warning('Cannot send ping message', exc_info=True) - except Exception: - if cron_mode: - return False # we don't want to see any stack trace in cron - else: - raise - return True - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import datetime +import logging +import sys +import urllib +import urllib2 +from openerp import pooler +from openerp import release +from openerp.osv import fields, osv +from openerp.tools.translate import _ +from openerp.tools.safe_eval import safe_eval +from openerp.tools.config import config +from openerp.tools import misc +_logger = logging.getLogger(__name__) +_PREVIOUS_LOG_CHECK = datetime.timedelta(days=365) + +def get_sys_logs(self, cr, uid): + """ + Utility method to send a publisher warranty get logs messages. + """ + pool = pooler.get_pool(cr.dbname) + dbuuid = pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') + db_create_date = pool.get('ir.config_parameter').get_param(cr, uid, 'database.create_date') + limit_date = datetime.datetime.now() + limit_date = limit_date - datetime.timedelta(15) + limit_date_str = limit_date.strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT) + nbr_users = pool.get('res.users').search(cr, uid, [], count=True) + nbr_active_users = pool.get('res.users').search(cr, uid, [('login_date', '>=', limit_date_str)], count=True) + nbr_share_users = False + nbr_active_share_users = False + if 'share' in pool.get('res.users')._all_columns: + nbr_share_users = pool.get('res.users').search(cr, uid, [('share', '=', True)], count=True) + nbr_active_share_users = pool.get('res.users').search(cr, uid, [('share', '=', True), ('login_date', '>=', limit_date_str)], count=True) + user = pool.get('res.users').browse(cr, uid, uid) + web_base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', 'False') + msg = {'dbuuid': dbuuid, + 'nbr_users': nbr_users, + 'nbr_active_users': nbr_active_users, + 'nbr_share_users': nbr_share_users, + 'nbr_active_share_users': nbr_active_share_users, + 'dbname': cr.dbname, + 'db_create_date': db_create_date, + 'version': release.version, + 'language': user.lang, + 'web_base_url': web_base_url} + msg.update(pool.get('res.company').read(cr, uid, [1], ['name', 'email', 'phone'])[0]) + add_arg = {'timeout': 30} if sys.version_info >= (2, 6) else {} + arguments = {'arg0': msg, + 'action': 'update'} + arguments_raw = urllib.urlencode(arguments) + url = config.get('publisher_warranty_url') + uo = urllib2.urlopen(url, arguments_raw, **add_arg) + result = {} + try: + submit_result = uo.read() + result = safe_eval(submit_result) + finally: + uo.close() + + return result + + +class publisher_warranty_contract(osv.osv): + _name = 'publisher_warranty.contract' + + def update_notification(self, cr, uid, ids, cron_mode = True, context = None): + """ + Send a message to OpenERP's publisher warranty server to check the + validity of the contracts, get notifications, etc... + + @param cron_mode: If true, catch all exceptions (appropriate for usage in a cron). + @type cron_mode: boolean + """ + try: + try: + result = get_sys_logs(self, cr, uid) + except Exception as ex: + if cron_mode: + return False + _logger.debug('Exception while sending a get logs messages', exc_info=1) + raise osv.except_osv(_('Error'), _('Error during communication with the publisher warranty server.')) + + limit_date = (datetime.datetime.now() - _PREVIOUS_LOG_CHECK).strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT) + proxy = self.pool.get('mail.message') + model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees') + for message in result['messages']: + values = {'body': message, + 'model': 'mail.group', + 'res_id': res_id, + 'user_id': False} + proxy.create(cr, uid, values, context=context) + + except Exception: + if cron_mode: + return False + raise + + return True \ No newline at end of file diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index 0079177a27a6e..5cc9aa5453b7e 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -1,81 +1,66 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2012-Today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see -# -############################################################################## - -from openerp import tools -from openerp.osv import osv -from openerp.osv import fields -from openerp.tools.translate import _ - - -class invite_wizard(osv.osv_memory): - """ Wizard to invite partners and make them followers. """ - _name = 'mail.wizard.invite' - _description = 'Invite wizard' - - def default_get(self, cr, uid, fields, context=None): - result = super(invite_wizard, self).default_get(cr, uid, fields, context=context) - if 'message' in fields and result.get('res_model') and result.get('res_id'): - document_name = self.pool.get(result.get('res_model')).name_get(cr, uid, [result.get('res_id')], context=context)[0][1] - message = _('
    You have been invited to follow %s.
    ') % document_name - result['message'] = message - elif 'message' in fields: - result['message'] = _('
    You have been invited to follow a new document.
    ') - return result - - _columns = { - 'res_model': fields.char('Related Document Model', size=128, - required=True, select=1, - help='Model of the followed resource'), - 'res_id': fields.integer('Related Document ID', select=1, - help='Id of the followed resource'), - 'partner_ids': fields.many2many('res.partner', string='Partners'), - 'message': fields.html('Message'), - } - - def add_followers(self, cr, uid, ids, context=None): - for wizard in self.browse(cr, uid, ids, context=context): - model_obj = self.pool.get(wizard.res_model) - document = model_obj.browse(cr, uid, wizard.res_id, context=context) - - # filter partner_ids to get the new followers, to avoid sending email to already following partners - new_follower_ids = [p.id for p in wizard.partner_ids if p.id not in document.message_follower_ids] - model_obj.message_subscribe(cr, uid, [wizard.res_id], new_follower_ids, context=context) - - # send an email only if a personal message exists - if wizard.message and not wizard.message == '
    ': # when deleting the message, cleditor keeps a
    - # add signature - user_id = self.pool.get("res.users").read(cr, uid, [uid], fields=["signature"], context=context)[0] - signature = user_id and user_id["signature"] or '' - if signature: - wizard.message = tools.append_content_to_html(wizard.message, signature, plaintext=True, container_tag='div') - # FIXME 8.0: use notification_email_send, send a wall message and let mail handle email notification + message box - for follower_id in new_follower_ids: - mail_mail = self.pool.get('mail.mail') - # the invite wizard should create a private message not related to any object -> no model, no res_id - mail_id = mail_mail.create(cr, uid, { - 'model': wizard.res_model, - 'res_id': wizard.res_id, - 'subject': _('Invitation to follow %s') % document.name_get()[0][1], - 'body_html': '%s' % wizard.message, - 'auto_delete': True, - }, context=context) - mail_mail.send(cr, uid, [mail_id], recipient_ids=[follower_id], context=context) - return {'type': 'ir.actions.act_window_close'} +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import tools +from openerp.osv import osv +from openerp.osv import fields +from openerp.tools.translate import _ + +class invite_wizard(osv.osv_memory): + """ Wizard to invite partners and make them followers. """ + _name = 'mail.wizard.invite' + _description = 'Invite wizard' + + def default_get(self, cr, uid, fields, context = None): + result = super(invite_wizard, self).default_get(cr, uid, fields, context=context) + if 'message' in fields and result.get('res_model') and result.get('res_id'): + document_name = self.pool.get(result.get('res_model')).name_get(cr, uid, [result.get('res_id')], context=context)[0][1] + message = _('
    You have been invited to follow %s.
    ' % document_name) + result['message'] = message + elif 'message' in fields: + result['message'] = _('
    You have been invited to follow a new document.
    ') + return result + + _columns = {'res_model': fields.char('Related Document Model', size=128, required=True, select=1, help='Model of the followed resource'), + 'res_id': fields.integer('Related Document ID', select=1, help='Id of the followed resource'), + 'partner_ids': fields.many2many('res.partner', string='Partners'), + 'message': fields.html('Message')} + + def add_followers(self, cr, uid, ids, context = None): + for wizard in self.browse(cr, uid, ids, context=context): + model_obj = self.pool.get(wizard.res_model) + document = model_obj.browse(cr, uid, wizard.res_id, context=context) + new_follower_ids = [ p.id for p in wizard.partner_ids if p.id not in document.message_follower_ids ] + model_obj.message_subscribe(cr, uid, [wizard.res_id], new_follower_ids, context=context) + if wizard.message and not wizard.message == '
    ': + user_id = self.pool.get('res.users').read(cr, uid, [uid], fields=['signature'], context=context)[0] + signature = user_id and user_id['signature'] or '' + if signature: + wizard.message = tools.append_content_to_html(wizard.message, signature, plaintext=True, container_tag='div') + for follower_id in new_follower_ids: + mail_mail = self.pool.get('mail.mail') + mail_id = mail_mail.create(cr, uid, {'model': wizard.res_model, + 'res_id': wizard.res_id, + 'subject': 'Invitation to follow %s' % document.name_get()[0][1], + 'body_html': '%s' % wizard.message, + 'auto_delete': True}, context=context) + mail_mail.send(cr, uid, [mail_id], recipient_ids=[follower_id], context=context) + + return {'type': 'ir.actions.act_window_close'} \ No newline at end of file diff --git a/addons/mail/wizard/invite_view.xml b/addons/mail/wizard/invite_view.xml index e957d546d37c3..e7f273508b948 100644 --- a/addons/mail/wizard/invite_view.xml +++ b/addons/mail/wizard/invite_view.xml @@ -4,10 +4,10 @@ - Add Followers + Thêm người theo dõi mail.wizard.invite - + @@ -17,10 +17,10 @@
    -
    diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 733057de1cf4e..c9d332127417e 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -1,304 +1,249 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-Today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see -# -############################################################################## - -import base64 -import re -from openerp import tools -from openerp import SUPERUSER_ID -from openerp.osv import osv -from openerp.osv import fields -from openerp.tools.safe_eval import safe_eval as eval -from openerp.tools.translate import _ - -# main mako-like expression pattern -EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') - - -class mail_compose_message(osv.TransientModel): - """ Generic message composition wizard. You may inherit from this wizard - at model and view levels to provide specific features. - - The behavior of the wizard depends on the composition_mode field: - - 'reply': reply to a previous message. The wizard is pre-populated - via ``get_message_data``. - - 'comment': new post on a record. The wizard is pre-populated via - ``get_record_data`` - - 'mass_mail': wizard in mass mailing mode where the mail details can - contain template placeholders that will be merged with actual data - before being sent to each recipient. - """ - _name = 'mail.compose.message' - _inherit = 'mail.message' - _description = 'Email composition wizard' - _log_access = True - - def default_get(self, cr, uid, fields, context=None): - """ Handle composition mode. Some details about context keys: - - comment: default mode, model and ID of a record the user comments - - default_model or active_model - - default_res_id or active_id - - reply: active_id of a message the user replies to - - default_parent_id or message_id or active_id: ID of the - mail.message we reply to - - message.res_model or default_model - - message.res_id or default_res_id - - mass_mail: model and IDs of records the user mass-mails - - active_ids: record IDs - - default_model or active_model - """ - if context is None: - context = {} - result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) - - # get some important values from context - composition_mode = context.get('default_composition_mode', context.get('mail.compose.message.mode')) - model = context.get('default_model', context.get('active_model')) - res_id = context.get('default_res_id', context.get('active_id')) - message_id = context.get('default_parent_id', context.get('message_id', context.get('active_id'))) - active_ids = context.get('active_ids') - - # get default values according to the composition mode - if composition_mode == 'reply': - vals = self.get_message_data(cr, uid, message_id, context=context) - elif composition_mode == 'comment' and model and res_id: - vals = self.get_record_data(cr, uid, model, res_id, context=context) - elif composition_mode == 'mass_mail' and model and active_ids: - vals = {'model': model, 'res_id': res_id} - else: - vals = {'model': model, 'res_id': res_id} - if composition_mode: - vals['composition_mode'] = composition_mode - - for field in vals: - if field in fields: - result[field] = vals[field] - - # TDE HACK: as mailboxes used default_model='res.users' and default_res_id=uid - # (because of lack of an accessible pid), creating a message on its own - # profile may crash (res_users does not allow writing on it) - # Posting on its own profile works (res_users redirect to res_partner) - # but when creating the mail.message to create the mail.compose.message - # access rights issues may rise - # We therefore directly change the model and res_id - if result.get('model') == 'res.users' and result.get('res_id') == uid: - result['model'] = 'res.partner' - result['res_id'] = self.pool.get('res.users').browse(cr, uid, uid).partner_id.id - return result - - def _get_composition_mode_selection(self, cr, uid, context=None): - return [('comment', 'Comment a document'), ('reply', 'Reply to a message'), ('mass_mail', 'Mass mailing')] - - _columns = { - 'composition_mode': fields.selection( - lambda s, *a, **k: s._get_composition_mode_selection(*a, **k), - string='Composition mode'), - 'partner_ids': fields.many2many('res.partner', - 'mail_compose_message_res_partner_rel', - 'wizard_id', 'partner_id', 'Additional contacts'), - 'attachment_ids': fields.many2many('ir.attachment', - 'mail_compose_message_ir_attachments_rel', - 'wizard_id', 'attachment_id', 'Attachments'), - 'filter_id': fields.many2one('ir.filters', 'Filters'), - } - - _defaults = { - 'composition_mode': 'comment', - 'body': lambda self, cr, uid, ctx={}: '', - 'subject': lambda self, cr, uid, ctx={}: False, - 'partner_ids': lambda self, cr, uid, ctx={}: [], - } - - def check_access_rule(self, cr, uid, ids, operation, context=None): - """ Access rules of mail.compose.message: - - create: if - - model, no res_id, I create a message in mass mail mode - - then: fall back on mail.message acces rules - """ - if isinstance(ids, (int, long)): - ids = [ids] - - # Author condition (CREATE (mass_mail)) - if operation == 'create' and uid != SUPERUSER_ID: - # read mail_compose_message.ids to have their values - message_values = {} - cr.execute('SELECT DISTINCT id, model, res_id FROM "%s" WHERE id = ANY (%%s) AND res_id = 0' % self._table, (ids,)) - for id, rmod, rid in cr.fetchall(): - message_values[id] = {'model': rmod, 'res_id': rid} - # remove from the set to check the ids that mail_compose_message accepts - author_ids = [mid for mid, message in message_values.iteritems() - if message.get('model') and not message.get('res_id')] - ids = list(set(ids) - set(author_ids)) - - return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context) - - def _notify(self, cr, uid, newid, context=None): - """ Override specific notify method of mail.message, because we do - not want that feature in the wizard. """ - return - - def get_record_data(self, cr, uid, model, res_id, context=None): - """ Returns a defaults-like dict with initial values for the composition - wizard when sending an email related to the document record - identified by ``model`` and ``res_id``. - - :param str model: model name of the document record this mail is - related to. - :param int res_id: id of the document record this mail is related to - """ - doc_name_get = self.pool.get(model).name_get(cr, uid, [res_id], context=context) - record_name = False - if doc_name_get: - record_name = doc_name_get[0][1] - values = { - 'model': model, - 'res_id': res_id, - 'record_name': record_name, - } - if record_name: - values['subject'] = 'Re: %s' % record_name - return values - - def get_message_data(self, cr, uid, message_id, context=None): - """ Returns a defaults-like dict with initial values for the composition - wizard when replying to the given message (e.g. including the quote - of the initial message, and the correct recipients). - - :param int message_id: id of the mail.message to which the user - is replying. - """ - if not message_id: - return {} - if context is None: - context = {} - message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context) - - # create subject - re_prefix = _('Re:') - reply_subject = tools.ustr(message_data.subject or message_data.record_name or '') - if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)) and message_data.subject: - reply_subject = "%s %s" % (re_prefix, reply_subject) - # get partner_ids from original message - partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else [] - partner_ids += context.get('default_partner_ids', []) - - # update the result - result = { - 'record_name': message_data.record_name, - 'model': message_data.model, - 'res_id': message_data.res_id, - 'parent_id': message_data.id, - 'subject': reply_subject, - 'partner_ids': partner_ids, - } - return result - - #------------------------------------------------------ - # Wizard validation and send - #------------------------------------------------------ - - def send_mail(self, cr, uid, ids, context=None): - """ Process the wizard content and proceed with sending the related - email(s), rendering any template patterns on the fly if needed. """ - if context is None: - context = {} - ir_attachment_obj = self.pool.get('ir.attachment') - active_ids = context.get('active_ids') - is_log = context.get('mail_compose_log', False) - - for wizard in self.browse(cr, uid, ids, context=context): - mass_mail_mode = wizard.composition_mode == 'mass_mail' - active_model_pool_name = wizard.model if wizard.model else 'mail.thread' - active_model_pool = self.pool.get(active_model_pool_name) - - # wizard works in batch mode: [res_id] or active_ids - res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id] - for res_id in res_ids: - # mail.message values, according to the wizard options - post_values = { - 'subject': wizard.subject, - 'body': wizard.body or '', - 'parent_id': wizard.parent_id and wizard.parent_id.id, - 'partner_ids': [partner.id for partner in wizard.partner_ids], - 'attachment_ids': [attach.id for attach in wizard.attachment_ids], - 'attachments': [], - } - # mass mailing: render and override default values - if mass_mail_mode and wizard.model: - email_dict = self.render_message(cr, uid, wizard, res_id, context=context) - post_values['partner_ids'] += email_dict.pop('partner_ids', []) - for filename, attachment_data in email_dict.pop('attachments', []): - # decode as render message return in base64 while message_post expect binary - post_values['attachments'].append((filename, base64.b64decode(attachment_data))) - attachment_ids = [] - for attach_id in post_values.pop('attachment_ids'): - new_attach_id = ir_attachment_obj.copy(cr, uid, attach_id, {'res_model': self._name, 'res_id': wizard.id}, context=context) - attachment_ids.append(new_attach_id) - post_values['attachment_ids'] = attachment_ids - post_values.update(email_dict) - # post the message - subtype = 'mail.mt_comment' - if is_log: # log a note: subtype is False - subtype = False - elif mass_mail_mode: # mass mail: is a log pushed to recipients, author not added - subtype = False - context = dict(context, mail_create_nosubscribe=True) # add context key to avoid subscribing the author - msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values) - # mass_mailing: notify specific partners, because subtype was False, and no-one was notified - if mass_mail_mode and post_values['partner_ids']: - self.pool.get('mail.notification')._notify(cr, uid, msg_id, post_values['partner_ids'], context=context) - - return {'type': 'ir.actions.act_window_close'} - - def render_message(self, cr, uid, wizard, res_id, context=None): - """ Generate an email from the template for given (wizard.model, res_id) - pair. This method is meant to be inherited by email_template that - will produce a more complete dictionary. """ - return { - 'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context), - 'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context), - } - - def render_template(self, cr, uid, template, model, res_id, context=None): - """ Render the given template text, replace mako-like expressions ``${expr}`` - with the result of evaluating these expressions with an evaluation context - containing: - - * ``user``: browse_record of the current user - * ``object``: browse_record of the document record this mail is - related to - * ``context``: the context passed to the mail composition wizard - - :param str template: the template text to render - :param str model: model name of the document record this mail is related to. - :param int res_id: id of the document record this mail is related to. - """ - if context is None: - context = {} - - def merge(match): - exp = str(match.group()[2:-1]).strip() - result = eval(exp, { - 'user': self.pool.get('res.users').browse(cr, uid, uid, context=context), - 'object': self.pool.get(model).browse(cr, uid, res_id, context=context), - 'context': dict(context), # copy context to prevent side-effects of eval - }) - return result and tools.ustr(result) or '' - return template and EXPRESSION_PATTERN.sub(merge, template) +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import re +from openerp import tools +from openerp import SUPERUSER_ID +from openerp.osv import osv +from openerp.osv import fields +from openerp.tools.safe_eval import safe_eval as eval +from openerp.tools.translate import _ +EXPRESSION_PATTERN = re.compile('(\\$\\{.+?\\})') + +class mail_compose_message(osv.TransientModel): + """ Generic message composition wizard. You may inherit from this wizard + at model and view levels to provide specific features. + + The behavior of the wizard depends on the composition_mode field: + - 'reply': reply to a previous message. The wizard is pre-populated + via ``get_message_data``. + - 'comment': new post on a record. The wizard is pre-populated via + ``get_record_data`` + - 'mass_mail': wizard in mass mailing mode where the mail details can + contain template placeholders that will be merged with actual data + before being sent to each recipient. + """ + _name = 'mail.compose.message' + _inherit = 'mail.message' + _description = 'Email composition wizard' + _log_access = True + + def default_get(self, cr, uid, fields, context = None): + """ Handle composition mode. Some details about context keys: + - comment: default mode, model and ID of a record the user comments + - default_model or active_model + - default_res_id or active_id + - reply: active_id of a message the user replies to + - default_parent_id or message_id or active_id: ID of the + mail.message we reply to + - message.res_model or default_model + - message.res_id or default_res_id + - mass_mail: model and IDs of records the user mass-mails + - active_ids: record IDs + - default_model or active_model + """ + if context is None: + context = {} + result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) + composition_mode = context.get('default_composition_mode', context.get('mail.compose.message.mode')) + model = context.get('default_model', context.get('active_model')) + res_id = context.get('default_res_id', context.get('active_id')) + message_id = context.get('default_parent_id', context.get('message_id', context.get('active_id'))) + active_ids = context.get('active_ids') + if composition_mode == 'reply': + vals = self.get_message_data(cr, uid, message_id, context=context) + elif composition_mode == 'comment' and model and res_id: + vals = self.get_record_data(cr, uid, model, res_id, context=context) + elif composition_mode == 'mass_mail' and model and active_ids: + vals = {'model': model, + 'res_id': res_id} + else: + vals = {'model': model, + 'res_id': res_id} + if composition_mode: + vals['composition_mode'] = composition_mode + for field in vals: + if field in fields: + result[field] = vals[field] + + if result.get('model') == 'res.users' and result.get('res_id') == uid: + result['model'] = 'res.partner' + result['res_id'] = self.pool.get('res.users').browse(cr, uid, uid).partner_id.id + return result + + def _get_composition_mode_selection(self, cr, uid, context = None): + return [('comment', 'Comment a document'), ('reply', 'Reply to a message'), ('mass_mail', 'Mass mailing')] + + _columns = {'composition_mode': fields.selection(lambda s, *a, **k: s._get_composition_mode_selection(*a, **k), string='Composition mode'), + 'partner_ids': fields.many2many('res.partner', 'mail_compose_message_res_partner_rel', 'wizard_id', 'partner_id', 'Additional contacts'), + 'attachment_ids': fields.many2many('ir.attachment', 'mail_compose_message_ir_attachments_rel', 'wizard_id', 'attachment_id', 'Attachments'), + 'filter_id': fields.many2one('ir.filters', 'Filters')} + _defaults = {'composition_mode': 'comment', + 'body': lambda self, cr, uid, ctx = {}: '', + 'subject': lambda self, cr, uid, ctx = {}: False, + 'partner_ids': lambda self, cr, uid, ctx = {}: []} + + def check_access_rule(self, cr, uid, ids, operation, context = None): + """ Access rules of mail.compose.message: + - create: if + - model, no res_id, I create a message in mass mail mode + - then: fall back on mail.message acces rules + """ + if isinstance(ids, (int, long)): + ids = [ids] + if operation == 'create' and uid != SUPERUSER_ID: + message_values = {} + cr.execute('SELECT DISTINCT id, model, res_id FROM "%s" WHERE id = ANY (%%s) AND res_id = 0' % self._table, (ids,)) + for id, rmod, rid in cr.fetchall(): + message_values[id] = {'model': rmod, + 'res_id': rid} + + author_ids = [ mid for mid, message in message_values.iteritems() if message.get('model') and not message.get('res_id') ] + ids = list(set(ids) - set(author_ids)) + return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context) + + def _notify(self, cr, uid, newid, context = None): + """ Override specific notify method of mail.message, because we do + not want that feature in the wizard. """ + pass + + def get_record_data(self, cr, uid, model, res_id, context = None): + """ Returns a defaults-like dict with initial values for the composition + wizard when sending an email related to the document record + identified by ``model`` and ``res_id``. + + :param str model: model name of the document record this mail is + related to. + :param int res_id: id of the document record this mail is related to + """ + doc_name_get = self.pool.get(model).name_get(cr, uid, [res_id], context=context) + record_name = False + if doc_name_get: + record_name = doc_name_get[0][1] + values = {'model': model, + 'res_id': res_id, + 'record_name': record_name} + if record_name: + values['subject'] = 'Re: %s' % record_name + return values + + def get_message_data(self, cr, uid, message_id, context = None): + """ Returns a defaults-like dict with initial values for the composition + wizard when replying to the given message (e.g. including the quote + of the initial message, and the correct recipients). + + :param int message_id: id of the mail.message to which the user + is replying. + """ + if not message_id: + return {} + else: + if context is None: + context = {} + message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context) + re_prefix = _('Re:') + reply_subject = tools.ustr(message_data.subject or message_data.record_name or '') + if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)) and message_data.subject: + reply_subject = '%s %s' % (re_prefix, reply_subject) + partner_ids = [ partner.id for partner in message_data.partner_ids ] if message_data.partner_ids else [] + partner_ids += context.get('default_partner_ids', []) + result = {'record_name': message_data.record_name, + 'model': message_data.model, + 'res_id': message_data.res_id, + 'parent_id': message_data.id, + 'subject': reply_subject, + 'partner_ids': partner_ids} + return result + + def send_mail(self, cr, uid, ids, context = None): + """ Process the wizard content and proceed with sending the related + email(s), rendering any template patterns on the fly if needed. """ + if context is None: + context = {} + ir_attachment_obj = self.pool.get('ir.attachment') + active_ids = context.get('active_ids') + is_log = context.get('mail_compose_log', False) + for wizard in self.browse(cr, uid, ids, context=context): + mass_mail_mode = wizard.composition_mode == 'mass_mail' + active_model_pool_name = wizard.model if wizard.model else 'mail.thread' + active_model_pool = self.pool.get(active_model_pool_name) + res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id] + for res_id in res_ids: + post_values = {'subject': wizard.subject, + 'body': wizard.body, + 'parent_id': wizard.parent_id and wizard.parent_id.id, + 'partner_ids': [ partner.id for partner in wizard.partner_ids ], + 'attachment_ids': [ attach.id for attach in wizard.attachment_ids ]} + if mass_mail_mode and wizard.model: + email_dict = self.render_message(cr, uid, wizard, res_id, context=context) + post_values['partner_ids'] += email_dict.pop('partner_ids', []) + post_values['attachments'] = email_dict.pop('attachments', []) + attachment_ids = [] + for attach_id in post_values.pop('attachment_ids'): + new_attach_id = ir_attachment_obj.copy(cr, uid, attach_id, {'res_model': self._name, + 'res_id': wizard.id}, context=context) + attachment_ids.append(new_attach_id) + + post_values['attachment_ids'] = attachment_ids + post_values.update(email_dict) + subtype = 'mail.mt_comment' + if is_log: + subtype = False + elif mass_mail_mode: + subtype = False + context = dict(context, mail_create_nosubscribe=True) + msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values) + if mass_mail_mode and post_values['partner_ids']: + self.pool.get('mail.notification')._notify(cr, uid, msg_id, post_values['partner_ids'], context=context) + + return {'type': 'ir.actions.act_window_close'} + + def render_message(self, cr, uid, wizard, res_id, context = None): + """ Generate an email from the template for given (wizard.model, res_id) + pair. This method is meant to be inherited by email_template that + will produce a more complete dictionary. """ + return {'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context), + 'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context)} + + def render_template(self, cr, uid, template, model, res_id, context = None): + """ Render the given template text, replace mako-like expressions ``${expr}`` + with the result of evaluating these expressions with an evaluation context + containing: + + * ``user``: browse_record of the current user + * ``object``: browse_record of the document record this mail is + related to + * ``context``: the context passed to the mail composition wizard + + :param str template: the template text to render + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. + """ + if context is None: + context = {} + + def merge(match): + exp = str(match.group()[2:-1]).strip() + result = eval(exp, {'user': self.pool.get('res.users').browse(cr, uid, uid, context=context), + 'object': self.pool.get(model).browse(cr, uid, res_id, context=context), + 'context': dict(context)}) + return result and tools.ustr(result) or '' + + return template and EXPRESSION_PATTERN.sub(merge, template) \ No newline at end of file diff --git a/addons/mail_message.py b/addons/mail_message.py new file mode 100644 index 0000000000000..8898b1a7f4cd0 --- /dev/null +++ b/addons/mail_message.py @@ -0,0 +1,940 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## + +import logging +from openerp import tools + +from email.header import decode_header +from openerp import SUPERUSER_ID +from openerp.osv import osv, orm, fields +from openerp.tools import html_email_clean +from openerp.tools.translate import _ + +_logger = logging.getLogger(__name__) + +try: + from mako.template import Template as MakoTemplate +except ImportError: + _logger.warning("payment_acquirer: mako templates not available, payment acquirer will not work!") + + +""" Some tools for parsing / creating email fields """ +def decode(text): + """Returns unicode() string conversion of the the given encoded smtp header text""" + if text: + text = decode_header(text.replace('\r', '')) + # The joining space will not be needed as of Python 3.3 + # See https://hg.python.org/cpython/rev/8c03fe231877 + return ' '.join([tools.ustr(x[0], x[1]) for x in text]) + + +class mail_message(osv.Model): + """ Messages model: system notification (replacing res.log notifications), + comments (OpenChatter discussion) and incoming emails. """ + _name = 'mail.message' + _description = 'Message' + _inherit = ['ir.needaction_mixin'] + _order = 'id desc' + _rec_name = 'record_name' + + _message_read_limit = 30 + _message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from', + 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name'] + _message_record_name_length = 18 + _message_read_more_limit = 1024 + + def default_get(self, cr, uid, fields, context=None): + # protection for `default_type` values leaking from menu action context (e.g. for invoices) + if context and context.get('default_type') and context.get('default_type') not in [ + val[0] for val in self._columns['type'].selection]: + context = dict(context, default_type=None) + return super(mail_message, self).default_get(cr, uid, fields, context=context) + + def _shorten_name(self, name): + if len(name) <= (self._message_record_name_length + 3): + return name + return name[:self._message_record_name_length] + '...' + + def _get_record_name(self, cr, uid, ids, name, arg, context=None): + """ Return the related document name, using name_get. It is done using + SUPERUSER_ID, to be sure to have the record name correctly stored. """ + # TDE note: regroup by model/ids, to have less queries to perform + result = dict.fromkeys(ids, False) + for message in self.read(cr, uid, ids, ['model', 'res_id'], context=context): + if not message.get('model') or not message.get('res_id') or not self.pool.get(message['model']): + continue + result[message['id']] = self.pool.get(message['model']).name_get(cr, SUPERUSER_ID, [message['res_id']], context=context)[0][1] + return result + + def _get_to_read(self, cr, uid, ids, name, arg, context=None): + """ Compute if the message is unread by the current user. """ + res = dict((id, False) for id in ids) + partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id', 'in', [partner_id]), + ('message_id', 'in', ids), + ('read', '=', False), + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.id] = True + return res + + def _search_to_read(self, cr, uid, obj, name, domain, context=None): + """ Search for messages to read by the current user. Condition is + inversed because we search unread message on a read column. """ + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.read', '=', not domain[0][2])] + + def _get_starred(self, cr, uid, ids, name, arg, context=None): + """ Compute if the message is unread by the current user. """ + res = dict((id, False) for id in ids) + partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id', 'in', [partner_id]), + ('message_id', 'in', ids), + ('starred', '=', True), + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.id] = True + return res + + def _search_starred(self, cr, uid, obj, name, domain, context=None): + """ Search for messages to read by the current user. Condition is + inversed because we search unread message on a read column. """ + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.starred', '=', domain[0][2])] + + def name_get(self, cr, uid, ids, context=None): + # name_get may receive int id instead of an id list + if isinstance(ids, (int, long)): + ids = [ids] + res = [] + for message in self.browse(cr, uid, ids, context=context): + name = '%s: %s' % (message.subject or '', message.body or '') + res.append((message.id, self._shorten_name(name.lstrip(' :')))) + return res + + _columns = { + 'type': fields.selection([ + ('email', 'Email'), + ('comment', 'Comment'), + ('notification', 'System notification'), + ], 'Type', + help="Message type: email for email message, notification for system "\ + "message, comment for other messages such as user replies"), + 'email_from': fields.char('From', + help="Email address of the sender. This field is set when no matching partner is found for incoming emails."), + 'author_id': fields.many2one('res.partner', 'Author', select=1, + ondelete='set null', + help="Author of the message. If not set, email_from may hold an email address that did not match any partner."), + 'partner_ids': fields.many2many('res.partner', string='Recipients'), + 'notified_partner_ids': fields.many2many('res.partner', 'mail_notification', + 'message_id', 'partner_id', 'Notified partners', + help='Partners that have a notification pushing this message in their mailboxes'), + 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', + 'message_id', 'attachment_id', 'Attachments'), + 'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, + ondelete='set null', help="Initial thread message."), + 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), + 'model': fields.char('Related Document Model', size=128, select=1), + 'res_id': fields.integer('Related Document ID', select=1), + 'record_name': fields.function(_get_record_name, type='char', + store=True, string='Message Record Name', + help="Name get of the related document."), + 'notification_ids': fields.one2many('mail.notification', 'message_id', + string='Notifications', auto_join=True, + help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'), + 'subject': fields.char('Subject'), + 'date': fields.datetime('Date'), + 'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1), + 'body': fields.html('Contents', help='Automatically sanitized HTML contents'), + 'to_read': fields.function(_get_to_read, fnct_search=_search_to_read, + type='boolean', string='To read', + help='Current user has an unread notification linked to this message'), + 'starred': fields.function(_get_starred, fnct_search=_search_starred, + type='boolean', string='Starred', + help='Current user has a starred notification linked to this message'), + 'subtype_id': fields.many2one('mail.message.subtype', 'Subtype', + ondelete='set null', select=1,), + 'vote_user_ids': fields.many2many('res.users', 'mail_vote', + 'message_id', 'user_id', string='Votes', + help='Users that voted for this message'), + } + + def _needaction_domain_get(self, cr, uid, context=None): + return [('to_read', '=', True)] + + def _get_default_author(self, cr, uid, context=None): + return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + + _defaults = { + 'type': 'email', + 'date': lambda *a: fields.datetime.now(), + 'author_id': lambda self, cr, uid, ctx={}: self._get_default_author(cr, uid, ctx), + 'body': '', + } + + #------------------------------------------------------ + # Vote/Like + #------------------------------------------------------ + + def vote_toggle(self, cr, uid, ids, context=None): + ''' Toggles vote. Performed using read to avoid access rights issues. + Done as SUPERUSER_ID because uid may vote for a message he cannot modify. ''' + for message in self.read(cr, uid, ids, ['vote_user_ids'], context=context): + new_has_voted = not (uid in message.get('vote_user_ids')) + if new_has_voted: + self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(4, uid)]}, context=context) + else: + self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(3, uid)]}, context=context) + return new_has_voted or False + + #------------------------------------------------------ + # download an attachment + #------------------------------------------------------ + + def download_attachment(self, cr, uid, id_message, attachment_id, context=None): + """ Return the content of linked attachments. """ + message = self.browse(cr, uid, id_message, context=context) + if attachment_id in [attachment.id for attachment in message.attachment_ids]: + attachment = self.pool.get('ir.attachment').browse(cr, SUPERUSER_ID, attachment_id, context=context) + if attachment.datas and attachment.datas_fname: + return { + 'base64': attachment.datas, + 'filename': attachment.datas_fname, + } + return False + + #------------------------------------------------------ + # Notification API + #------------------------------------------------------ + + def set_message_read(self, cr, uid, msg_ids, read, create_missing=True, context=None): + """ Set messages as (un)read. Technically, the notifications related + to uid are set to (un)read. If for some msg_ids there are missing + notifications (i.e. due to load more or thread parent fetching), + they are created. + + :param bool read: set notification as (un)read + :param bool create_missing: create notifications for missing entries + (i.e. when acting on displayed messages not notified) + + :return number of message mark as read + """ + notification_obj = self.pool.get('mail.notification') + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + domain = [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)] + if not create_missing: + domain += [('read', '=', not read)] + notif_ids = notification_obj.search(cr, uid, domain, context=context) + + # all message have notifications: already set them as (un)read + if len(notif_ids) == len(msg_ids) or not create_missing: + notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context) + return len(notif_ids) + + # some messages do not have notifications: find which one, create notification, update read status + notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)] + to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) + for msg_id in to_create_msg_ids: + notification_obj.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context) + notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context) + return len(notif_ids) + + def set_message_starred(self, cr, uid, msg_ids, starred, create_missing=True, context=None): + """ Set messages as (un)starred. Technically, the notifications related + to uid are set to (un)starred. + + :param bool starred: set notification as (un)starred + :param bool create_missing: create notifications for missing entries + (i.e. when acting on displayed messages not notified) + """ + notification_obj = self.pool.get('mail.notification') + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + domain = [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)] + if not create_missing: + domain += [('starred', '=', not starred)] + values = { + 'starred': starred + } + if starred: + values['read'] = False + + notif_ids = notification_obj.search(cr, uid, domain, context=context) + + # all message have notifications: already set them as (un)starred + if len(notif_ids) == len(msg_ids) or not create_missing: + notification_obj.write(cr, uid, notif_ids, values, context=context) + return starred + + # some messages do not have notifications: find which one, create notification, update starred status + notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)] + to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) + for msg_id in to_create_msg_ids: + notification_obj.create(cr, uid, dict(values, partner_id=user_pid, message_id=msg_id), context=context) + notification_obj.write(cr, uid, notif_ids, values, context=context) + return starred + + #------------------------------------------------------ + # Message loading for web interface + #------------------------------------------------------ + + def _message_read_dict_postprocess(self, cr, uid, messages, message_tree, context=None): + """ Post-processing on values given by message_read. This method will + handle partners in batch to avoid doing numerous queries. + + :param list messages: list of message, as get_dict result + :param dict message_tree: {[msg.id]: msg browse record} + """ + res_partner_obj = self.pool.get('res.partner') + ir_attachment_obj = self.pool.get('ir.attachment') + pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0] + + # 1. Aggregate partners (author_id and partner_ids) and attachments + partner_ids = set() + attachment_ids = set() + for key, message in message_tree.iteritems(): + if message.author_id: + partner_ids |= set([message.author_id.id]) + if message.notified_partner_ids: + partner_ids |= set([partner.id for partner in message.notified_partner_ids]) + if message.attachment_ids: + attachment_ids |= set([attachment.id for attachment in message.attachment_ids]) + # Read partners as SUPERUSER -> display the names like classic m2o even if no access + partners = res_partner_obj.name_get(cr, SUPERUSER_ID, list(partner_ids), context=context) + partner_tree = dict((partner[0], partner) for partner in partners) + + # 2. Attachments as SUPERUSER, because could receive msg and attachments for doc uid cannot see + attachments = ir_attachment_obj.read(cr, SUPERUSER_ID, list(attachment_ids), ['id', 'datas_fname', 'name'], context=context) + attachments_tree = dict((attachment['id'], {'id': attachment['id'], 'filename': attachment['datas_fname'], 'name': attachment['name']}) for attachment in attachments) + + # 3. Update message dictionaries + for message_dict in messages: + message_id = message_dict.get('id') + message = message_tree[message_id] + if message.author_id: + author = partner_tree[message.author_id.id] + else: + author = (0, message.email_from) + partner_ids = [] + for partner in message.notified_partner_ids: + if partner.id in partner_tree: + partner_ids.append(partner_tree[partner.id]) + attachment_ids = [] + for attachment in message.attachment_ids: + if attachment.id in attachments_tree: + attachment_ids.append(attachments_tree[attachment.id]) + message_dict.update({ + 'is_author': pid == author[0], + 'author_id': author, + 'partner_ids': partner_ids, + 'attachment_ids': attachment_ids, + }) + return True + + def _message_read_dict(self, cr, uid, message, parent_id=False, context=None): + """ Return a dict representation of the message. This representation is + used in the JS client code, to display the messages. Partners and + attachments related stuff will be done in post-processing in batch. + + :param dict message: mail.message browse record + """ + # private message: no model, no res_id + is_private = False + if not message.model or not message.res_id: + is_private = True + # votes and favorites: res.users ids, no prefetching should be done + vote_nb = len(message.vote_user_ids) + has_voted = uid in [user.id for user in message.vote_user_ids] + + try: + body_html = html_email_clean(message.body) + except Exception: + body_html = '

    Encoding Error :
    Unable to convert this message (id: %s).

    ' % message.id + _logger.exception(Exception) + + return {'id': message.id, + 'type': message.type, + 'subtype': message.subtype_id.name if message.subtype_id else False, + 'body': body_html, + 'model': message.model, + 'res_id': message.res_id, + 'record_name': message.record_name, + 'subject': message.subject, + 'date': message.date, + 'to_read': message.to_read, + 'parent_id': parent_id, + 'is_private': is_private, + 'author_id': False, + 'is_author': False, + 'partner_ids': [], + 'vote_nb': vote_nb, + 'has_voted': has_voted, + 'is_favorite': message.starred, + 'attachment_ids': [], + } + + def _message_read_add_expandables(self, cr, uid, messages, message_tree, parent_tree, + message_unload_ids=[], thread_level=0, domain=[], parent_id=False, context=None): + """ Create expandables for message_read, to load new messages. + 1. get the expandable for new threads + if display is flat (thread_level == 0): + fetch message_ids < min(already displayed ids), because we + want a flat display, ordered by id + else: + fetch message_ids that are not childs of already displayed + messages + 2. get the expandables for new messages inside threads if display + is not flat + for each thread header, search for its childs + for each hole in the child list based on message displayed, + create an expandable + + :param list messages: list of message structure for the Chatter + widget to which expandables are added + :param dict message_tree: dict [id]: browse record of this message + :param dict parent_tree: dict [parent_id]: [child_ids] + :param list message_unload_ids: list of message_ids we do not want + to load + :return bool: True + """ + def _get_expandable(domain, message_nb, parent_id, max_limit): + return { + 'domain': domain, + 'nb_messages': message_nb, + 'type': 'expandable', + 'parent_id': parent_id, + 'max_limit': max_limit, + } + + if not messages: + return True + message_ids = sorted(message_tree.keys()) + + # 1. get the expandable for new threads + if thread_level == 0: + exp_domain = domain + [('id', '<', min(message_unload_ids + message_ids))] + else: + exp_domain = domain + ['!', ('id', 'child_of', message_unload_ids + parent_tree.keys())] + ids = self.search(cr, uid, exp_domain, context=context, limit=1) + if ids: + # inside a thread: prepend + if parent_id: + messages.insert(0, _get_expandable(exp_domain, -1, parent_id, True)) + # new threads: append + else: + messages.append(_get_expandable(exp_domain, -1, parent_id, True)) + + # 2. get the expandables for new messages inside threads if display is not flat + if thread_level == 0: + return True + for message_id in message_ids: + message = message_tree[message_id] + + # generate only for thread header messages (TDE note: parent_id may be False is uid cannot see parent_id, seems ok) + if message.parent_id: + continue + + # check there are message for expandable + child_ids = set([child.id for child in message.child_ids]) - set(message_unload_ids) + child_ids = sorted(list(child_ids), reverse=True) + if not child_ids: + continue + + # make groups of unread messages + id_min, id_max, nb = max(child_ids), 0, 0 + for child_id in child_ids: + if not child_id in message_ids: + nb += 1 + if id_min > child_id: + id_min = child_id + if id_max < child_id: + id_max = child_id + elif nb > 0: + exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)] + idx = [msg.get('id') for msg in messages].index(child_id) + 1 + # messages.append(_get_expandable(exp_domain, nb, message_id, False)) + messages.insert(idx, _get_expandable(exp_domain, nb, message_id, False)) + id_min, id_max, nb = max(child_ids), 0, 0 + else: + id_min, id_max, nb = max(child_ids), 0, 0 + if nb > 0: + exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)] + idx = [msg.get('id') for msg in messages].index(message_id) + 1 + # messages.append(_get_expandable(exp_domain, nb, message_id, id_min)) + messages.insert(idx, _get_expandable(exp_domain, nb, message_id, False)) + + return True + + def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None, + thread_level=0, context=None, parent_id=False, limit=None): + """ Read messages from mail.message, and get back a list of structured + messages to be displayed as discussion threads. If IDs is set, + fetch these records. Otherwise use the domain to fetch messages. + After having fetch messages, their ancestors will be added to obtain + well formed threads, if uid has access to them. + + After reading the messages, expandable messages are added in the + message list (see ``_message_read_add_expandables``). It consists + in messages holding the 'read more' data: number of messages to + read, domain to apply. + + :param list ids: optional IDs to fetch + :param list domain: optional domain for searching ids if ids not set + :param list message_unload_ids: optional ids we do not want to fetch, + because i.e. they are already displayed somewhere + :param int parent_id: context of parent_id + - if parent_id reached when adding ancestors, stop going further + in the ancestor search + - if set in flat mode, ancestor_id is set to parent_id + :param int limit: number of messages to fetch, before adding the + ancestors and expandables + :return list: list of message structure for the Chatter widget + """ + assert thread_level in [0, 1], 'message_read() thread_level should be 0 (flat) or 1 (1 level of thread); given %s.' % thread_level + domain = domain if domain is not None else [] + message_unload_ids = message_unload_ids if message_unload_ids is not None else [] + if message_unload_ids: + domain += [('id', 'not in', message_unload_ids)] + notification_obj = self.pool.get('mail.notification') + limit = limit or self._message_read_limit + message_tree = {} + message_list = [] + parent_tree = {} + + # no specific IDS given: fetch messages according to the domain, add their parents if uid has access to + if ids is None: + ids = self.search(cr, uid, domain, context=context, limit=limit) + + # fetch parent if threaded, sort messages + for message in self.browse(cr, uid, ids, context=context): + message_id = message.id + if message_id in message_tree: + continue + message_tree[message_id] = message + + # find parent_id + if thread_level == 0: + tree_parent_id = parent_id + else: + tree_parent_id = message_id + parent = message + while parent.parent_id and parent.parent_id.id != parent_id: + parent = parent.parent_id + tree_parent_id = parent.id + if not parent.id in message_tree: + message_tree[parent.id] = parent + # newest messages first + parent_tree.setdefault(tree_parent_id, []) + if tree_parent_id != message_id: + parent_tree[tree_parent_id].append(self._message_read_dict(cr, uid, message_tree[message_id], parent_id=tree_parent_id, context=context)) + + if thread_level: + for key, message_id_list in parent_tree.iteritems(): + message_id_list.sort(key=lambda item: item['id']) + message_id_list.insert(0, self._message_read_dict(cr, uid, message_tree[key], context=context)) + + # create final ordered message_list based on parent_tree + parent_list = parent_tree.items() + parent_list = sorted(parent_list, key=lambda item: max([msg.get('id') for msg in item[1]]) if item[1] else item[0], reverse=True) + message_list = [message for (key, msg_list) in parent_list for message in msg_list] + + # get the child expandable messages for the tree + self._message_read_dict_postprocess(cr, uid, message_list, message_tree, context=context) + self._message_read_add_expandables(cr, uid, message_list, message_tree, parent_tree, + thread_level=thread_level, message_unload_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context) + return message_list + + #------------------------------------------------------ + # mail_message internals + #------------------------------------------------------ + + def init(self, cr): + cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'mail_message_model_res_id_idx'""") + if not cr.fetchone(): + cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""") + + def _find_allowed_model_wise(self, cr, uid, doc_model, doc_dict, context=None): + doc_ids = doc_dict.keys() + ctx = dict(context or {}, active_test=False) + allowed_doc_ids = self.pool.get(doc_model).search(cr, uid, [('id', 'in', doc_ids)], context=ctx) + return set([message_id for allowed_doc_id in allowed_doc_ids for message_id in doc_dict[allowed_doc_id]]) + + def _find_allowed_doc_ids(self, cr, uid, model_ids, context=None): + model_access_obj = self.pool.get('ir.model.access') + allowed_ids = set() + for doc_model, doc_dict in model_ids.iteritems(): + if not model_access_obj.check(cr, uid, doc_model, 'read', False): + continue + allowed_ids |= self._find_allowed_model_wise(cr, uid, doc_model, doc_dict, context=context) + return allowed_ids + + def _search(self, cr, uid, args, offset=0, limit=None, order=None, + context=None, count=False, access_rights_uid=None): + """ Override that adds specific access rights of mail.message, to remove + ids uid could not see according to our custom rules. Please refer + to check_access_rule for more details about those rules. + + After having received ids of a classic search, keep only: + - if author_id == pid, uid is the author, OR + - a notification (id, pid) exists, uid has been notified, OR + - uid have read access to the related document is model, res_id + - otherwise: remove the id + """ + # Rules do not apply to administrator + if uid == SUPERUSER_ID: + return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order, + context=context, count=count, access_rights_uid=access_rights_uid) + # Perform a super with count as False, to have the ids, not a counter + ids = super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order, + context=context, count=False, access_rights_uid=access_rights_uid) + if not ids and count: + return 0 + elif not ids: + return ids + + pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'])['partner_id'][0] + author_ids, partner_ids, allowed_ids = set([]), set([]), set([]) + model_ids = {} + + messages = super(mail_message, self).read(cr, uid, ids, ['author_id', 'model', 'res_id', 'notified_partner_ids'], context=context) + for message in messages: + if message.get('author_id') and message.get('author_id')[0] == pid: + author_ids.add(message.get('id')) + elif pid in message.get('notified_partner_ids'): + partner_ids.add(message.get('id')) + elif message.get('model') and message.get('res_id'): + model_ids.setdefault(message.get('model'), {}).setdefault(message.get('res_id'), set()).add(message.get('id')) + + allowed_ids = self._find_allowed_doc_ids(cr, uid, model_ids, context=context) + final_ids = author_ids | partner_ids | allowed_ids + + if count: + return len(final_ids) + else: + # re-construct a list based on ids, because set did not keep the original order + id_list = [id for id in ids if id in final_ids] + return id_list + + def check_access_rule(self, cr, uid, ids, operation, context=None): + """ Access rules of mail.message: + - read: if + - author_id == pid, uid is the author, OR + - mail_notification (id, pid) exists, uid has been notified, OR + - uid have read access to the related document if model, res_id + - otherwise: raise + - create: if + - no model, no res_id, I create a private message OR + - pid in message_follower_ids if model, res_id OR + - mail_notification (parent_id.id, pid) exists, uid has been notified of the parent, OR + - uid have write or create access on the related document if model, res_id, OR + - otherwise: raise + - write: if + - author_id == pid, uid is the author, OR + - uid has write or create access on the related document if model, res_id + - otherwise: raise + - unlink: if + - uid has write or create access on the related document if model, res_id + - otherwise: raise + """ + def _generate_model_record_ids(msg_val, msg_ids=[]): + """ :param model_record_ids: {'model': {'res_id': (msg_id, msg_id)}, ... } + :param message_values: {'msg_id': {'model': .., 'res_id': .., 'author_id': ..}} + """ + model_record_ids = {} + for id in msg_ids: + if msg_val[id]['model'] and msg_val[id]['res_id']: + model_record_ids.setdefault(msg_val[id]['model'], dict()).setdefault(msg_val[id]['res_id'], set()).add(msg_val[id]['res_id']) + return model_record_ids + + if uid == SUPERUSER_ID: + return + if isinstance(ids, (int, long)): + ids = [ids] + not_obj = self.pool.get('mail.notification') + fol_obj = self.pool.get('mail.followers') + partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0] + + # Read mail_message.ids to have their values + message_values = dict.fromkeys(ids) + cr.execute('SELECT DISTINCT id, model, res_id, author_id, parent_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,)) + for id, rmod, rid, author_id, parent_id in cr.fetchall(): + message_values[id] = {'model': rmod, 'res_id': rid, 'author_id': author_id, 'parent_id': parent_id} + + # Author condition (READ, WRITE, CREATE (private)) -> could become an ir.rule ? + author_ids = [] + if operation == 'read' or operation == 'write': + author_ids = [mid for mid, message in message_values.iteritems() + if message.get('author_id') and message.get('author_id') == partner_id] + elif operation == 'create': + author_ids = [mid for mid, message in message_values.iteritems() + if not message.get('model') and not message.get('res_id')] + + # Parent condition, for create (check for received notifications for the created message parent) + notified_ids = [] + if operation == 'create': + parent_ids = [message.get('parent_id') for mid, message in message_values.iteritems() + if message.get('parent_id')] + not_ids = not_obj.search(cr, SUPERUSER_ID, [('message_id.id', 'in', parent_ids), ('partner_id', '=', partner_id)], context=context) + not_parent_ids = [notif.message_id.id for notif in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)] + notified_ids += [mid for mid, message in message_values.iteritems() + if message.get('parent_id') in not_parent_ids] + + # Notification condition, for read (check for received notifications and create (in message_follower_ids)) -> could become an ir.rule, but not till we do not have a many2one variable field + other_ids = set(ids).difference(set(author_ids), set(notified_ids)) + model_record_ids = _generate_model_record_ids(message_values, other_ids) + if operation == 'read': + not_ids = not_obj.search(cr, SUPERUSER_ID, [ + ('partner_id', '=', partner_id), + ('message_id', 'in', ids), + ], context=context) + notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)] + elif operation == 'create': + for doc_model, doc_dict in model_record_ids.items(): + fol_ids = fol_obj.search(cr, SUPERUSER_ID, [ + ('res_model', '=', doc_model), + ('res_id', 'in', list(doc_dict.keys())), + ('partner_id', '=', partner_id), + ], context=context) + fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)] + notified_ids += [mid for mid, message in message_values.iteritems() + if message.get('model') == doc_model and message.get('res_id') in fol_mids] + + # CRUD: Access rights related to the document + other_ids = other_ids.difference(set(notified_ids)) + model_record_ids = _generate_model_record_ids(message_values, other_ids) + document_related_ids = [] + for model, doc_dict in model_record_ids.items(): + model_obj = self.pool.get(model) + mids = model_obj.exists(cr, uid, doc_dict.keys()) + if operation in ['create', 'write', 'unlink']: + if not model_obj.check_access_rights(cr, uid, 'write', raise_exception=False): + model_obj.check_access_rights(cr, uid, 'create') + model_obj.check_access_rule(cr, uid, mids, 'write', context=context) + else: + model_obj.check_access_rights(cr, uid, operation) + model_obj.check_access_rule(cr, uid, mids, operation, context=context) + document_related_ids += [mid for mid, message in message_values.iteritems() + if message.get('model') == model and message.get('res_id') in mids] + + # Calculate remaining ids: if not void, raise an error + other_ids = other_ids.difference(set(document_related_ids)) + if not other_ids: + return + raise orm.except_orm(_('Access Denied'), + _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \ + (self._description, operation)) + + def create(self, cr, uid, values, context=None): + if context is None: + context = {} + default_starred = context.pop('default_starred', False) + if not values.get('message_id') and values.get('res_id') and values.get('model'): + values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) + elif not values.get('message_id'): + values['message_id'] = tools.generate_tracking_message_id('private') + newid = super(mail_message, self).create(cr, uid, values, context) + self._notify(cr, uid, newid, context=context) + # TDE FIXME: handle default_starred. Why not setting an inv on starred ? + # Because starred will call set_message_starred, that looks for notifications. + # When creating a new mail_message, it will create a notification to a message + # that does not exist, leading to an error (key not existing). Also this + # this means unread notifications will be created, yet we can not assure + # this is what we want. + if default_starred: + self.set_message_starred(cr, uid, [newid], True, context=context) + return newid + + def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'): + """ Override to explicitely call check_access_rule, that is not called + by the ORM. It instead directly fetches ir.rules and apply them. """ + self.check_access_rule(cr, uid, ids, 'read', context=context) + res = super(mail_message, self).read(cr, uid, ids, fields=fields, context=context, load=load) + return res + + def unlink(self, cr, uid, ids, context=None): + # cascade-delete attachments that are directly attached to the message (should only happen + # for mail.messages that act as parent for a standalone mail.mail record). + self.check_access_rule(cr, uid, ids, 'unlink', context=context) + attachments_to_delete = [] + for message in self.browse(cr, uid, ids, context=context): + for attach in message.attachment_ids: + if attach.res_model == self._name and (attach.res_id == message.id or attach.res_id == 0): + attachments_to_delete.append(attach.id) + if attachments_to_delete: + self.pool.get('ir.attachment').unlink(cr, uid, attachments_to_delete, context=context) + return super(mail_message, self).unlink(cr, uid, ids, context=context) + + def copy(self, cr, uid, id, default=None, context=None): + """ Overridden to avoid duplicating fields that are unique to each email """ + if default is None: + default = {} + default.update(message_id=False, headers=False) + return super(mail_message, self).copy(cr, uid, id, default=default, context=context) + + #------------------------------------------------------ + # Messaging API + #------------------------------------------------------ + + # TDE note: this code is not used currently, will be improved in a future merge, when quoted context + # will be added to email send for notifications. Currently only WIP. + MAIL_TEMPLATE = """
    + % if message: + ${display_message(message)} + % endif + % for ctx_msg in context_messages: + ${display_message(ctx_msg)} + % endfor + % if add_expandable: + ${display_expandable()} + % endif + ${display_message(header_message)} +
    + + <%def name="display_message(message)"> +
    + Subject: ${message.subject}
    + Body: ${message.body} +
    + + + <%def name="display_expandable()"> +
    This is an expandable.
    + + """ + + def message_quote_context(self, cr, uid, id, context=None, limit=3, add_original=False): + """ + 1. message.parent_id = False: new thread, no quote_context + 2. get the lasts messages in the thread before message + 3. get the message header + 4. add an expandable between them + + :param dict quote_context: options for quoting + :return string: html quote + """ + add_expandable = False + + message = self.browse(cr, uid, id, context=context) + if not message.parent_id: + return '' + context_ids = self.search(cr, uid, [ + ('parent_id', '=', message.parent_id.id), + ('id', '<', message.id), + ], limit=limit, context=context) + + if len(context_ids) >= limit: + add_expandable = True + context_ids = context_ids[0:-1] + + context_ids.append(message.parent_id.id) + context_messages = self.browse(cr, uid, context_ids, context=context) + header_message = context_messages.pop() + + try: + if not add_original: + message = False + result = MakoTemplate(self.MAIL_TEMPLATE).render_unicode(message=message, + context_messages=context_messages, + header_message=header_message, + add_expandable=add_expandable, + # context kw would clash with mako internals + ctx=context, + format_exceptions=True) + result = result.strip() + return result + except Exception: + _logger.exception("failed to render mako template for quoting message") + return '' + return result + + def _notify(self, cr, uid, newid, context=None): + """ Add the related record followers to the destination partner_ids if is not a private message. + Call mail_notification.notify to manage the email sending + """ + notification_obj = self.pool.get('mail.notification') + message = self.browse(cr, uid, newid, context=context) + + partners_to_notify = set([]) + # message has no subtype_id: pure log message -> no partners, no one notified + if not message.subtype_id: + return True + + # all followers of the mail.message document have to be added as partners and notified + if message.model and message.res_id: + fol_obj = self.pool.get("mail.followers") + # browse as SUPERUSER because rules could restrict the search results + fol_ids = fol_obj.search(cr, SUPERUSER_ID, [ + ('res_model', '=', message.model), + ('res_id', '=', message.res_id), + ], context=context) + partners_to_notify |= set( + fo.partner_id for fo in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context) + if message.subtype_id.id in [st.id for st in fo.subtype_ids] + ) + # remove me from notified partners, unless the message is written on my own wall + if message.author_id and message.model == "res.partner" and message.res_id == message.author_id.id: + partners_to_notify |= set([message.author_id]) + elif message.author_id: + partners_to_notify -= set([message.author_id]) + + # all partner_ids of the mail.message have to be notified regardless of the above (even the author if explicitly added!) + if message.partner_ids: + partners_to_notify |= set(message.partner_ids) + + # notify + if partners_to_notify: + notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context) + message.refresh() + + # An error appear when a user receive a notification without notifying + # the parent message -> add a read notification for the parent + if message.parent_id: + # all notified_partner_ids of the mail.message have to be notified for the parented messages + partners_to_parent_notify = set(message.notified_partner_ids).difference(message.parent_id.notified_partner_ids) + for partner in partners_to_parent_notify: + notification_obj.create(cr, uid, { + 'message_id': message.parent_id.id, + 'partner_id': partner.id, + 'read': True, + }, context=context) + + #------------------------------------------------------ + # Tools + #------------------------------------------------------ + + def check_partners_email(self, cr, uid, partner_ids, context=None): + """ Verify that selected partner_ids have an email_address defined. + Otherwise throw a warning. """ + partner_wo_email_lst = [] + for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context): + if not partner.email: + partner_wo_email_lst.append(partner) + if not partner_wo_email_lst: + return {} + warning_msg = _('The following partners chosen as recipients for the email have no email address linked :') + for partner in partner_wo_email_lst: + warning_msg += '\n- %s' % (partner.name) + return {'warning': { + 'title': _('Partners email addresses not found'), + 'message': warning_msg, + } + } diff --git a/addons/marketing/images/config_marketing.jpeg b/addons/marketing/images/config_marketing.jpeg new file mode 100644 index 0000000000000..9f689a233bfac Binary files /dev/null and b/addons/marketing/images/config_marketing.jpeg differ diff --git a/addons/marketing/images/marketing-hover.png b/addons/marketing/images/marketing-hover.png new file mode 100644 index 0000000000000..7bfc2f1106b3e Binary files /dev/null and b/addons/marketing/images/marketing-hover.png differ diff --git a/addons/marketing/images/marketing.png b/addons/marketing/images/marketing.png new file mode 100644 index 0000000000000..e37fefa78f310 Binary files /dev/null and b/addons/marketing/images/marketing.png differ diff --git a/addons/marketing_campaign/__openerp__.py b/addons/marketing_campaign/__openerp__.py index 22ed4abaef1ef..a51347fd1bbe4 100644 --- a/addons/marketing_campaign/__openerp__.py +++ b/addons/marketing_campaign/__openerp__.py @@ -40,7 +40,7 @@ send, reports to print and send by email, custom actions * Define input segments that will select the items that should enter the campaign (e.g leads from certain countries.) - * Run your campaign in simulation mode to test it real-time or accelerated, + * Run you campaign in simulation mode to test it real-time or accelerated, and fine-tune it * You may also start the real campaign in manual mode, where each action requires manual validation diff --git a/addons/marketing_campaign/images/campaign.png b/addons/marketing_campaign/images/campaign.png new file mode 100644 index 0000000000000..09b4d13194978 Binary files /dev/null and b/addons/marketing_campaign/images/campaign.png differ diff --git a/addons/marketing_campaign/images/campaigns.jpeg b/addons/marketing_campaign/images/campaigns.jpeg new file mode 100644 index 0000000000000..15deadb65760b Binary files /dev/null and b/addons/marketing_campaign/images/campaigns.jpeg differ diff --git a/addons/marketing_campaign/images/email_account.jpeg b/addons/marketing_campaign/images/email_account.jpeg new file mode 100644 index 0000000000000..d7b122a17410b Binary files /dev/null and b/addons/marketing_campaign/images/email_account.jpeg differ diff --git a/addons/marketing_campaign/images/email_templates.jpeg b/addons/marketing_campaign/images/email_templates.jpeg new file mode 100644 index 0000000000000..0bfc135bfc12f Binary files /dev/null and b/addons/marketing_campaign/images/email_templates.jpeg differ diff --git a/addons/marketing_campaign/images/segments.jpeg b/addons/marketing_campaign/images/segments.jpeg new file mode 100644 index 0000000000000..29da318852412 Binary files /dev/null and b/addons/marketing_campaign/images/segments.jpeg differ diff --git a/addons/marketing_campaign/marketing_campaign_demo.xml b/addons/marketing_campaign/marketing_campaign_demo.xml index 376981b9b4111..92443fe987db6 100644 --- a/addons/marketing_campaign/marketing_campaign_demo.xml +++ b/addons/marketing_campaign/marketing_campaign_demo.xml @@ -6,7 +6,7 @@ welcome new partner info@openerp.com Welcome to the OpenERP Partner Channel! - ${(object.email or '')|safe} + ${object.email or ''} Hello, you will receive your welcome pack via email shortly.
    @@ -14,7 +14,7 @@ congrats silver partner info@openerp.com Congratulations! You are now a Silver Partner! - ${(object.email or '')|safe} + ${object.email or ''} Hi, we are delighted to welcome you among our Silver Partners as of today! @@ -23,7 +23,7 @@ congrats gold partner info@openerp.com Congratulations! You are now one of our Gold Partners! - ${(object.email or '')|safe} + ${object.email or ''} Hi, we are delighted to let you know that you have entered the select circle of our Gold Partners diff --git a/addons/marketing_campaign_crm_demo/images/campaigns.jpeg b/addons/marketing_campaign_crm_demo/images/campaigns.jpeg new file mode 100644 index 0000000000000..7984cd902dca4 Binary files /dev/null and b/addons/marketing_campaign_crm_demo/images/campaigns.jpeg differ diff --git a/addons/marketing_campaign_crm_demo/images/email_templates.jpeg b/addons/marketing_campaign_crm_demo/images/email_templates.jpeg new file mode 100644 index 0000000000000..fa91ef37ac251 Binary files /dev/null and b/addons/marketing_campaign_crm_demo/images/email_templates.jpeg differ diff --git a/addons/membership/images/members.jpeg b/addons/membership/images/members.jpeg new file mode 100644 index 0000000000000..b6d93b1ea53ed Binary files /dev/null and b/addons/membership/images/members.jpeg differ diff --git a/addons/membership/images/membership_list.jpeg b/addons/membership/images/membership_list.jpeg new file mode 100644 index 0000000000000..e47be8f93847e Binary files /dev/null and b/addons/membership/images/membership_list.jpeg differ diff --git a/addons/membership/images/membership_products.jpeg b/addons/membership/images/membership_products.jpeg new file mode 100644 index 0000000000000..0291ed85de926 Binary files /dev/null and b/addons/membership/images/membership_products.jpeg differ diff --git a/addons/membership/membership_demo.xml b/addons/membership/membership_demo.xml index 8d82f12ffd66c..49863bb1f4c35 100644 --- a/addons/membership/membership_demo.xml +++ b/addons/membership/membership_demo.xml @@ -6,7 +6,7 @@ True - + Golden Membership 180 @@ -16,7 +16,7 @@ True - + Silver Membership 80 @@ -26,7 +26,7 @@ True - + Basic Membership 40 diff --git a/addons/membership/membership_view.xml b/addons/membership/membership_view.xml index 266866c64669d..ffcbe1bae37d7 100644 --- a/addons/membership/membership_view.xml +++ b/addons/membership/membership_view.xml @@ -161,8 +161,6 @@ 50 - @@ -261,7 +259,7 @@ - + diff --git a/addons/mrp/__openerp__.py b/addons/mrp/__openerp__.py index 3157eee57bc81..6e025ae2d90ea 100644 --- a/addons/mrp/__openerp__.py +++ b/addons/mrp/__openerp__.py @@ -74,12 +74,12 @@ 'res_config_view.xml', ], 'demo': ['mrp_demo.xml'], + #TODO: This yml tests are needed to be completely reviewed again because the product wood panel is removed in product demo as it does not suit for new demo context of computer and consultant company + # so the ymls are too complex to change at this stage 'test': [ - 'test/bom_with_service_type_product.yml', - 'test/mrp_users.yml', - 'test/order_demo.yml', - 'test/order_process.yml', - 'test/cancel_order.yml', +# 'test/order_demo.yml', +# 'test/order_process.yml', +# 'test/cancel_order.yml', ], 'installable': True, 'application': True, diff --git a/addons/mrp/company.py b/addons/mrp/company.py index af8a21f49344b..810ce505d3891 100644 --- a/addons/mrp/company.py +++ b/addons/mrp/company.py @@ -1,36 +1,29 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import osv,fields - -class company(osv.osv): - _inherit = 'res.company' - _columns = { - 'manufacturing_lead': fields.float('Manufacturing Lead Time', required=True, - help="Security days for each manufacturing operation."), - } - _defaults = { - 'manufacturing_lead': lambda *a: 1.0, - } -company() - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import osv, fields + +class company(osv.osv): + _inherit = 'res.company' + _columns = {'manufacturing_lead': fields.float('Manufacturing Lead Time', required=True, help='Security days for each manufacturing operation.')} + _defaults = {'manufacturing_lead': lambda *a: 1.0} + + +company() \ No newline at end of file diff --git a/addons/mrp/images/bill_of_materials.jpeg b/addons/mrp/images/bill_of_materials.jpeg new file mode 100644 index 0000000000000..da3403082d1d2 Binary files /dev/null and b/addons/mrp/images/bill_of_materials.jpeg differ diff --git a/addons/mrp/images/manufacturing-hover.png b/addons/mrp/images/manufacturing-hover.png new file mode 100644 index 0000000000000..747596e78c126 Binary files /dev/null and b/addons/mrp/images/manufacturing-hover.png differ diff --git a/addons/mrp/images/manufacturing.png b/addons/mrp/images/manufacturing.png new file mode 100644 index 0000000000000..29d8468e16cba Binary files /dev/null and b/addons/mrp/images/manufacturing.png differ diff --git a/addons/mrp/images/manufacturing_analysis.jpeg b/addons/mrp/images/manufacturing_analysis.jpeg new file mode 100644 index 0000000000000..2b56fce523b21 Binary files /dev/null and b/addons/mrp/images/manufacturing_analysis.jpeg differ diff --git a/addons/mrp/images/manufacturing_order.jpeg b/addons/mrp/images/manufacturing_order.jpeg new file mode 100644 index 0000000000000..b18a6c4fd5e26 Binary files /dev/null and b/addons/mrp/images/manufacturing_order.jpeg differ diff --git a/addons/mrp/images/planning_manufacturing_order.jpeg b/addons/mrp/images/planning_manufacturing_order.jpeg new file mode 100644 index 0000000000000..e79c396e7ae71 Binary files /dev/null and b/addons/mrp/images/planning_manufacturing_order.jpeg differ diff --git a/addons/mrp/images/production_dashboard.jpeg b/addons/mrp/images/production_dashboard.jpeg new file mode 100644 index 0000000000000..e3a9e82b27a29 Binary files /dev/null and b/addons/mrp/images/production_dashboard.jpeg differ diff --git a/addons/mrp/images/routings.jpeg b/addons/mrp/images/routings.jpeg new file mode 100644 index 0000000000000..82e8b77ee8d5b Binary files /dev/null and b/addons/mrp/images/routings.jpeg differ diff --git a/addons/mrp/images/work_centers.jpeg b/addons/mrp/images/work_centers.jpeg new file mode 100644 index 0000000000000..c464215680e34 Binary files /dev/null and b/addons/mrp/images/work_centers.jpeg differ diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 32c0114b16bc1..c72dbba1ed670 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -1,1164 +1,946 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time -from datetime import datetime - -import openerp.addons.decimal_precision as dp -from openerp.osv import fields, osv, orm -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT -from openerp.tools import float_compare -from openerp.tools.translate import _ -from openerp import netsvc -from openerp import tools -from openerp import SUPERUSER_ID -from openerp.addons.product import _common - -#---------------------------------------------------------- -# Work Centers -#---------------------------------------------------------- -# capacity_hour : capacity per hour. default: 1.0. -# Eg: If 5 concurrent operations at one time: capacity = 5 (because 5 employees) -# unit_per_cycle : how many units are produced for one cycle - -class mrp_workcenter(osv.osv): - _name = 'mrp.workcenter' - _description = 'Work Center' - _inherits = {'resource.resource':"resource_id"} - _columns = { - 'note': fields.text('Description', help="Description of the Work Center. Explain here what's a cycle according to this Work Center."), - 'capacity_per_cycle': fields.float('Capacity per Cycle', help="Number of operations this Work Center can do in parallel. If this Work Center represents a team of 5 workers, the capacity per cycle is 5."), - 'time_cycle': fields.float('Time for 1 cycle (hour)', help="Time in hours for doing one cycle."), - 'time_start': fields.float('Time before prod.', help="Time in hours for the setup."), - 'time_stop': fields.float('Time after prod.', help="Time in hours for the cleaning."), - 'costs_hour': fields.float('Cost per hour', help="Specify Cost of Work Center per hour."), - 'costs_hour_account_id': fields.many2one('account.analytic.account', 'Hour Account', domain=[('type','!=','view')], - help="Fill this only if you want automatic analytic accounting entries on production orders."), - 'costs_cycle': fields.float('Cost per cycle', help="Specify Cost of Work Center per cycle."), - 'costs_cycle_account_id': fields.many2one('account.analytic.account', 'Cycle Account', domain=[('type','!=','view')], - help="Fill this only if you want automatic analytic accounting entries on production orders."), - 'costs_journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal'), - 'costs_general_account_id': fields.many2one('account.account', 'General Account', domain=[('type','!=','view')]), - 'resource_id': fields.many2one('resource.resource','Resource', ondelete='cascade', required=True), - 'product_id': fields.many2one('product.product','Work Center Product', help="Fill this product to easily track your production costs in the analytic accounting."), - } - _defaults = { - 'capacity_per_cycle': 1.0, - 'resource_type': 'material', - } - - def on_change_product_cost(self, cr, uid, ids, product_id, context=None): - value = {} - - if product_id: - cost = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - value = {'costs_hour': cost.standard_price} - return {'value': value} - - def _check_capacity_per_cycle(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids, context=context): - if obj.capacity_per_cycle <= 0.0: - return False - return True - - _constraints = [ - (_check_capacity_per_cycle, 'The capacity per cycle must be strictly positive.', ['capacity_per_cycle']), - ] - -mrp_workcenter() - - -class mrp_routing(osv.osv): - """ - For specifying the routings of Work Centers. - """ - _name = 'mrp.routing' - _description = 'Routing' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the routing without removing it."), - 'code': fields.char('Code', size=8), - - 'note': fields.text('Description'), - 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Work Centers'), - - 'location_id': fields.many2one('stock.location', 'Production Location', - help="Keep empty if you produce at the location where the finished products are needed." \ - "Set a location if you produce at a fixed location. This can be a partner location " \ - "if you subcontract the manufacturing operations." - ), - 'company_id': fields.many2one('res.company', 'Company'), - } - _defaults = { - 'active': lambda *a: 1, - 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.routing', context=context) - } -mrp_routing() - -class mrp_routing_workcenter(osv.osv): - """ - Defines working cycles and hours of a Work Center using routings. - """ - _name = 'mrp.routing.workcenter' - _description = 'Work Center Usage' - _order = 'sequence' - _columns = { - 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), - 'name': fields.char('Name', size=64, required=True), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of routing Work Centers."), - 'cycle_nbr': fields.float('Number of Cycles', required=True, - help="Number of iterations this work center has to do in the specified operation of the routing."), - 'hour_nbr': fields.float('Number of Hours', required=True, help="Time in hours for this Work Center to achieve the operation of the specified routing."), - 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade', - help="Routing indicates all the Work Centers used, for how long and/or cycles." \ - "If Routing is indicated then,the third tab of a production order (Work Centers) will be automatically pre-completed."), - 'note': fields.text('Description'), - 'company_id': fields.related('routing_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), - } - _defaults = { - 'cycle_nbr': lambda *a: 1.0, - 'hour_nbr': lambda *a: 0.0, - } -mrp_routing_workcenter() - -class mrp_bom(osv.osv): - """ - Defines bills of material for a product. - """ - _name = 'mrp.bom' - _description = 'Bill of Material' - _inherit = ['mail.thread'] - - def _child_compute(self, cr, uid, ids, name, arg, context=None): - """ Gets child bom. - @param self: The object pointer - @param cr: The current row, from the database cursor, - @param uid: The current user ID for security checks - @param ids: List of selected IDs - @param name: Name of the field - @param arg: User defined argument - @param context: A standard dictionary for contextual values - @return: Dictionary of values - """ - result = {} - if context is None: - context = {} - bom_obj = self.pool.get('mrp.bom') - bom_id = context and context.get('active_id', False) or False - cr.execute('select id from mrp_bom') - if all(bom_id != r[0] for r in cr.fetchall()): - ids.sort() - bom_id = ids[0] - bom_parent = bom_obj.browse(cr, uid, bom_id, context=context) - for bom in self.browse(cr, uid, ids, context=context): - if (bom_parent) or (bom.id == bom_id): - result[bom.id] = map(lambda x: x.id, bom.bom_lines) - else: - result[bom.id] = [] - if bom.bom_lines: - continue - ok = ((name=='child_complete_ids') and (bom.product_id.supply_method=='produce')) - if (bom.type=='phantom' or ok): - sids = bom_obj.search(cr, uid, [('bom_id','=',False),('product_id','=',bom.product_id.id)]) - if sids: - bom2 = bom_obj.browse(cr, uid, sids[0], context=context) - result[bom.id] += map(lambda x: x.id, bom2.bom_lines) - - return result - - def _compute_type(self, cr, uid, ids, field_name, arg, context=None): - """ Sets particular method for the selected bom type. - @param field_name: Name of the field - @param arg: User defined argument - @return: Dictionary of values - """ - res = dict.fromkeys(ids, False) - for line in self.browse(cr, uid, ids, context=context): - if line.type == 'phantom' and not line.bom_id: - res[line.id] = 'set' - continue - if line.bom_lines or line.type == 'phantom': - continue - if line.product_id.supply_method == 'produce': - if line.product_id.procure_method == 'make_to_stock': - res[line.id] = 'stock' - else: - res[line.id] = 'order' - return res - - _columns = { - 'name': fields.char('Name', size=64), - 'code': fields.char('Reference', size=16), - 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the bills of material without removing it."), - 'type': fields.selection([('normal','Normal BoM'),('phantom','Sets / Phantom')], 'BoM Type', required=True, - help= "If a by-product is used in several products, it can be useful to create its own BoM. "\ - "Though if you don't want separated production orders for this by-product, select Set/Phantom as BoM type. "\ - "If a Phantom BoM is used for a root product, it will be sold and shipped as a set of components, instead of being produced."), - 'method': fields.function(_compute_type, string='Method', type='selection', selection=[('',''),('stock','On Stock'),('order','On Order'),('set','Set / Pack')]), - 'date_start': fields.date('Valid From', help="Validity of this BoM or component. Keep empty if it's always valid."), - 'date_stop': fields.date('Valid Until', help="Validity of this BoM or component. Keep empty if it's always valid."), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of bills of material."), - 'position': fields.char('Internal Reference', size=64, help="Reference to a position in an external plan."), - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'product_uos_qty': fields.float('Product UOS Qty'), - 'product_uos': fields.many2one('product.uom', 'Product UOS', help="Product UOS (Unit of Sale) is the unit of measurement for the invoicing and promotion of stock."), - 'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')), - 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"), - 'product_rounding': fields.float('Product Rounding', help="Rounding applied on the product quantity."), - 'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% within the production process."), - 'bom_lines': fields.one2many('mrp.bom', 'bom_id', 'BoM Lines'), - 'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True), - 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."), - 'property_ids': fields.many2many('mrp.property', 'mrp_bom_property_rel', 'bom_id','property_id', 'Properties'), - 'child_complete_ids': fields.function(_child_compute, relation='mrp.bom', string="BoM Hierarchy", type='many2many'), - 'company_id': fields.many2one('res.company','Company',required=True), - } - _defaults = { - 'active': lambda *a: 1, - 'product_efficiency': lambda *a: 1.0, - 'product_qty': lambda *a: 1.0, - 'product_rounding': lambda *a: 0.0, - 'type': lambda *a: 'normal', - 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.bom', context=c), - } - _order = "sequence" - _parent_name = "bom_id" - _sql_constraints = [ - ('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0.\n' \ - 'You should install the mrp_byproduct module if you want to manage extra products on BoMs !'), - ] - - def _check_recursion(self, cr, uid, ids, context=None): - level = 100 - while len(ids): - cr.execute('select distinct bom_id from mrp_bom where id IN %s',(tuple(ids),)) - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - def _check_product(self, cr, uid, ids, context=None): - boms = self.browse(cr, uid, ids, context=context) - def check_bom(boms, all_prod): - res = True - for bom in boms: - if bom.product_id.id in all_prod: - return False - if bom.bom_lines: - res = res and check_bom([b for b in bom.bom_lines if b not in boms], all_prod + [bom.product_id.id]) - return res - return check_bom(boms, []) - - - _constraints = [ - (_check_recursion, 'Error ! You cannot create recursive BoM.', ['parent_id']), - (_check_product, 'BoM line product should not be same as BoM product.', ['product_id']), - ] - - def onchange_product_id(self, cr, uid, ids, product_id, name, context=None): - """ Changes UoM and name if product_id changes. - @param name: Name of the field - @param product_id: Changed product_id - @return: Dictionary of changed values - """ - if product_id: - prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - return {'value': {'name': prod.name, 'product_uom': prod.uom_id.id}} - return {} - - def onchange_uom(self, cr, uid, ids, product_id, product_uom, context=None): - res = {'value':{}} - if not product_uom or not product_id: - return res - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) - if uom.category_id.id != product.uom_id.category_id.id: - res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} - res['value'].update({'product_uom': product.uom_id.id}) - return res - - def _bom_find(self, cr, uid, product_id, product_uom, properties=None): - """ Finds BoM for particular product and product uom. - @param product_id: Selected product. - @param product_uom: Unit of measure of a product. - @param properties: List of related properties. - @return: False or BoM id. - """ - if properties is None: - properties = [] - domain = [('product_id', '=', product_id), ('bom_id', '=', False), - '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT)), - '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT))] - ids = self.search(cr, uid, domain) - max_prop = 0 - result = False - for bom in self.pool.get('mrp.bom').browse(cr, uid, ids): - prop = 0 - for prop_id in bom.property_ids: - if prop_id.id in properties: - prop += 1 - if (prop > max_prop) or ((max_prop == 0) and not result): - result = bom.id - max_prop = prop - return result - - def _bom_explode(self, cr, uid, bom, factor, properties=None, addthis=False, level=0, routing_id=False): - """ Finds Products and Work Centers for related BoM for manufacturing order. - @param bom: BoM of particular product. - @param factor: Factor of product UoM. - @param properties: A List of properties Ids. - @param addthis: If BoM found then True else False. - @param level: Depth level to find BoM lines starts from 10. - @return: result: List of dictionaries containing product details. - result2: List of dictionaries containing Work Center details. - """ - routing_obj = self.pool.get('mrp.routing') - factor = factor / (bom.product_efficiency or 1.0) - factor = _common.ceiling(factor, bom.product_rounding) - if factor < bom.product_rounding: - factor = bom.product_rounding - result = [] - result2 = [] - phantom = False - if bom.type == 'phantom' and not bom.bom_lines: - newbom = self._bom_find(cr, uid, bom.product_id.id, bom.product_uom.id, properties) - - if newbom and newbom != bom.id: - res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0], factor*bom.product_qty, properties, addthis=True, level=level+10) - result = result + res[0] - result2 = result2 + res[1] - phantom = True - else: - phantom = False - if not phantom: - if addthis and not bom.bom_lines: - result.append( - { - 'name': bom.product_id.name, - 'product_id': bom.product_id.id, - 'product_qty': bom.product_qty * factor, - 'product_uom': bom.product_uom.id, - 'product_uos_qty': bom.product_uos and bom.product_uos_qty * factor or False, - 'product_uos': bom.product_uos and bom.product_uos.id or False, - }) - routing = (routing_id and routing_obj.browse(cr, uid, routing_id)) or bom.routing_id or False - if routing: - for wc_use in routing.workcenter_lines: - wc = wc_use.workcenter_id - d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle) - mult = (d + (m and 1.0 or 0.0)) - cycle = mult * wc_use.cycle_nbr - result2.append({ - 'name': tools.ustr(wc_use.name) + ' - ' + tools.ustr(bom.product_id.name), - 'workcenter_id': wc.id, - 'sequence': level+(wc_use.sequence or 0), - 'cycle': cycle, - 'hour': float(wc_use.hour_nbr*mult + ((wc.time_start or 0.0)+(wc.time_stop or 0.0)+cycle*(wc.time_cycle or 0.0)) * (wc.time_efficiency or 1.0)), - }) - for bom2 in bom.bom_lines: - if (bom2.date_start and bom2.date_start > time.strftime(DEFAULT_SERVER_DATE_FORMAT)) or \ - (bom2.date_stop and bom2.date_stop < time.strftime(DEFAULT_SERVER_DATE_FORMAT)): - continue - res = self._bom_explode(cr, uid, bom2, factor, properties, addthis=True, level=level+10) - result = result + res[0] - result2 = result2 + res[1] - - # We merge the results for the same product. The reason is that action_produce does not - # support the case where the same product appears on multiple lines. To avoid major changes - # in a stable version, we do this simple hack at this point. - # Only for v7.0, do not use in v8.0 - result_dict = {} - result_dup = False - for product_detail in result: - key = ( - product_detail['name'], - product_detail['product_id'], - product_detail['product_uom'], - product_detail['product_uos_qty'], - product_detail['product_uos'] - ) - if key in result_dict: - result_dict[key] += product_detail['product_qty'] - result_dup = True - else: - result_dict[key] = product_detail['product_qty'] - - if result_dup: - result = [] - for key in result_dict.keys(): - result.append( - { - 'name': key[0], - 'product_id': key[1], - 'product_qty': result_dict[key], - 'product_uom': key[2], - 'product_uos_qty': key[3], - 'product_uos': key[4], - }) - - return result, result2 - - def copy_data(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - bom_data = self.read(cr, uid, id, [], context=context) - default.update(name=_("%s (copy)") % (bom_data['name']), bom_id=False) - return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context) - - def unlink(self, cr, uid, ids, context=None): - if self.pool['mrp.production'].search(cr, uid, [ - ('bom_id', 'in', ids), ('state', 'not in', ['done', 'cancel']) - ], context=context): - raise osv.except_osv(_('Warning!'), _('You can not delete a Bill of Material with running manufacturing orders.\nPlease close or cancel it first.')) - return super(mrp_bom, self).unlink(cr, uid, ids, context=context) - - -def rounding(f, r): - # TODO for trunk: log deprecation warning - # _logger.warning("Deprecated rounding method, please use tools.float_round to round floats.") - import math - if not r: - return f - return math.ceil(f / r) * r - -class mrp_production(osv.osv): - """ - Production Orders / Manufacturing Orders - """ - _name = 'mrp.production' - _description = 'Manufacturing Order' - _date_name = 'date_planned' - _inherit = ['mail.thread', 'ir.needaction_mixin'] - - def _production_calc(self, cr, uid, ids, prop, unknow_none, context=None): - """ Calculates total hours and total no. of cycles for a production order. - @param prop: Name of field. - @param unknow_none: - @return: Dictionary of values. - """ - result = {} - for prod in self.browse(cr, uid, ids, context=context): - result[prod.id] = { - 'hour_total': 0.0, - 'cycle_total': 0.0, - } - for wc in prod.workcenter_lines: - result[prod.id]['hour_total'] += wc.hour - result[prod.id]['cycle_total'] += wc.cycle - return result - - def _src_id_default(self, cr, uid, ids, context=None): - try: - location_model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') - self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) - except (orm.except_orm, ValueError): - location_id = False - return location_id - - def _dest_id_default(self, cr, uid, ids, context=None): - try: - location_model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') - self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) - except (orm.except_orm, ValueError): - location_id = False - return location_id - - _columns = { - 'name': fields.char('Reference', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}), - 'origin': fields.char('Source Document', size=64, readonly=True, states={'draft': [('readonly', False)]}, - help="Reference of the document that generated this production order request."), - 'priority': fields.selection([('0','Not urgent'),('1','Normal'),('2','Urgent'),('3','Very Urgent')], 'Priority', - select=True, readonly=True, states=dict.fromkeys(['draft', 'confirmed'], [('readonly', False)])), - - 'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True, states={'draft': [('readonly', False)]}), - 'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}), - 'product_uos_qty': fields.float('Product UoS Quantity', readonly=True, states={'draft': [('readonly', False)]}), - 'product_uos': fields.many2one('product.uom', 'Product UoS', readonly=True, states={'draft': [('readonly', False)]}), - - 'location_src_id': fields.many2one('stock.location', 'Raw Materials Location', required=True, - readonly=True, states={'draft':[('readonly',False)]}, - help="Location where the system will look for components."), - 'location_dest_id': fields.many2one('stock.location', 'Finished Products Location', required=True, - readonly=True, states={'draft':[('readonly',False)]}, - help="Location where the system will stock the finished products."), - 'date_planned': fields.datetime('Scheduled Date', required=True, select=1, readonly=True, states={'draft':[('readonly',False)]}), - 'date_start': fields.datetime('Start Date', select=True, readonly=True), - 'date_finished': fields.datetime('End Date', select=True, readonly=True), - 'bom_id': fields.many2one('mrp.bom', 'Bill of Material', domain=[('bom_id','=',False)], readonly=True, states={'draft':[('readonly',False)]}, - help="Bill of Materials allow you to define the list of required raw materials to make a finished product."), - 'routing_id': fields.many2one('mrp.routing', string='Routing', on_delete='set null', readonly=True, states={'draft':[('readonly',False)]}, - help="The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production plannification."), - 'picking_id': fields.many2one('stock.picking', 'Picking List', readonly=True, ondelete="restrict", - help="This is the Internal Picking List that brings the finished product to the production plan"), - 'move_prod_id': fields.many2one('stock.move', 'Product Move', readonly=True), - 'move_lines': fields.many2many('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Products to Consume', - domain=[('state','not in', ('done', 'cancel'))], readonly=True, states={'draft':[('readonly',False)]}), - 'move_lines2': fields.many2many('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Consumed Products', - domain=[('state','in', ('done', 'cancel'))], readonly=True, states={'draft':[('readonly',False)]}), - 'move_created_ids': fields.one2many('stock.move', 'production_id', 'Products to Produce', - domain=[('state','not in', ('done', 'cancel'))], readonly=True, states={'draft':[('readonly',False)]}), - 'move_created_ids2': fields.one2many('stock.move', 'production_id', 'Produced Products', - domain=[('state','in', ('done', 'cancel'))], readonly=True, states={'draft':[('readonly',False)]}), - 'product_lines': fields.one2many('mrp.production.product.line', 'production_id', 'Scheduled goods', - readonly=True, states={'draft':[('readonly',False)]}), - 'workcenter_lines': fields.one2many('mrp.production.workcenter.line', 'production_id', 'Work Centers Utilisation', - readonly=True, states={'draft':[('readonly',False)]}), - 'state': fields.selection( - [('draft', 'New'), ('cancel', 'Cancelled'), ('picking_except', 'Picking Exception'), ('confirmed', 'Awaiting Raw Materials'), - ('ready', 'Ready to Produce'), ('in_production', 'Production Started'), ('done', 'Done')], - string='Status', readonly=True, - track_visibility='onchange', - help="When the production order is created the status is set to 'Draft'.\n\ - If the order is confirmed the status is set to 'Waiting Goods'.\n\ - If any exceptions are there, the status is set to 'Picking Exception'.\n\ - If the stock is available then the status is set to 'Ready to Produce'.\n\ - When the production gets started then the status is set to 'In Production'.\n\ - When the production is over, the status is set to 'Done'."), - 'hour_total': fields.function(_production_calc, type='float', string='Total Hours', multi='workorder', store=True), - 'cycle_total': fields.function(_production_calc, type='float', string='Total Cycles', multi='workorder', store=True), - 'user_id':fields.many2one('res.users', 'Responsible'), - 'company_id': fields.many2one('res.company','Company',required=True), - } - _defaults = { - 'priority': lambda *a: '1', - 'state': lambda *a: 'draft', - 'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'product_qty': lambda *a: 1.0, - 'user_id': lambda self, cr, uid, c: uid, - 'name': lambda x, y, z, c: x.pool.get('ir.sequence').get(y, z, 'mrp.production') or '/', - 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.production', context=c), - 'location_src_id': _src_id_default, - 'location_dest_id': _dest_id_default - } - _sql_constraints = [ - ('name_uniq', 'unique(name, company_id)', 'Reference must be unique per Company!'), - ] - _order = 'priority desc, date_planned asc'; - - def _check_qty(self, cr, uid, ids, context=None): - for order in self.browse(cr, uid, ids, context=context): - if order.product_qty <= 0: - return False - return True - - _constraints = [ - (_check_qty, 'Order quantity cannot be negative or zero!', ['product_qty']), - ] - - def unlink(self, cr, uid, ids, context=None): - for production in self.browse(cr, uid, ids, context=context): - if production.state not in ('draft', 'cancel'): - raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a manufacturing order in state \'%s\'.') % production.state) - return super(mrp_production, self).unlink(cr, uid, ids, context=context) - - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - default.update({ - 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'), - 'move_lines' : [], - 'move_lines2' : [], - 'move_created_ids' : [], - 'move_created_ids2' : [], - 'product_lines' : [], - 'move_prod_id' : False, - 'picking_id' : False - }) - return super(mrp_production, self).copy(cr, uid, id, default, context) - - def location_id_change(self, cr, uid, ids, src, dest, context=None): - """ Changes destination location if source location is changed. - @param src: Source location id. - @param dest: Destination location id. - @return: Dictionary of values. - """ - if dest: - return {} - if src: - return {'value': {'location_dest_id': src}} - return {} - - def product_id_change(self, cr, uid, ids, product_id, context=None): - """ Finds UoM of changed product. - @param product_id: Id of changed product. - @return: Dictionary of values. - """ - if not product_id: - return {'value': { - 'product_uom': False, - 'bom_id': False, - 'routing_id': False - }} - bom_obj = self.pool.get('mrp.bom') - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - bom_id = bom_obj._bom_find(cr, uid, product.id, product.uom_id and product.uom_id.id, []) - routing_id = False - if bom_id: - bom_point = bom_obj.browse(cr, uid, bom_id, context=context) - routing_id = bom_point.routing_id.id or False - - product_uom_id = product.uom_id and product.uom_id.id or False - result = { - 'product_uom': product_uom_id, - 'bom_id': bom_id, - 'routing_id': routing_id, - } - return {'value': result} - - def bom_id_change(self, cr, uid, ids, bom_id, context=None): - """ Finds routing for changed BoM. - @param product: Id of product. - @return: Dictionary of values. - """ - if not bom_id: - return {'value': { - 'routing_id': False - }} - bom_point = self.pool.get('mrp.bom').browse(cr, uid, bom_id, context=context) - routing_id = bom_point.routing_id.id or False - result = { - 'routing_id': routing_id - } - return {'value': result} - - def action_picking_except(self, cr, uid, ids): - """ Changes the state to Exception. - @return: True - """ - self.write(cr, uid, ids, {'state': 'picking_except'}) - return True - - def _prepare_lines(self, cr, uid, production, properties=None, context=None): - # search BoM structure and route - bom_obj = self.pool.get('mrp.bom') - uom_obj = self.pool.get('product.uom') - bom_point = production.bom_id - bom_id = production.bom_id.id - if not bom_point: - bom_id = bom_obj._bom_find(cr, uid, production.product_id.id, production.product_uom.id, properties) - if bom_id: - bom_point = bom_obj.browse(cr, uid, bom_id) - routing_id = bom_point.routing_id.id or False - self.write(cr, uid, [production.id], {'bom_id': bom_id, 'routing_id': routing_id}) - - if not bom_id: - raise osv.except_osv(_('Error!'), _("Cannot find a bill of material for this product.")) - - # get components and workcenter_lines from BoM structure - factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id) - return bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id) - - def _action_compute_lines(self, cr, uid, ids, properties=None, context=None): - """ Compute product_lines and workcenter_lines from BoM structure - @return: product_lines - """ - - if properties is None: - properties = [] - results = [] - prod_line_obj = self.pool.get('mrp.production.product.line') - workcenter_line_obj = self.pool.get('mrp.production.workcenter.line') - - for production in self.browse(cr, uid, ids, context=context): - #unlink product_lines - prod_line_obj.unlink(cr, SUPERUSER_ID, [line.id for line in production.product_lines], context=context) - - #unlink workcenter_lines - workcenter_line_obj.unlink(cr, SUPERUSER_ID, [line.id for line in production.workcenter_lines], context=context) - - res = self._prepare_lines(cr, uid, production, properties=properties, context=context) - results = res[0] # product_lines - results2 = res[1] # workcenter_lines - - # reset product_lines in production order - for line in results: - line['production_id'] = production.id - prod_line_obj.create(cr, uid, line) - - #reset workcenter_lines in production order - for line in results2: - line['production_id'] = production.id - workcenter_line_obj.create(cr, uid, line) - return results - - def action_compute(self, cr, uid, ids, properties=None, context=None): - """ Computes bills of material of a product. - @param properties: List containing dictionaries of properties. - @return: No. of products. - """ - return len(self._action_compute_lines(cr, uid, ids, properties=properties, context=context)) - - def action_cancel(self, cr, uid, ids, context=None): - """ Cancels the production order and related stock moves. - @return: True - """ - if context is None: - context = {} - move_obj = self.pool.get('stock.move') - for production in self.browse(cr, uid, ids, context=context): - if production.state == 'confirmed' and production.picking_id.state not in ('draft', 'cancel'): - raise osv.except_osv( - _('Cannot cancel manufacturing order!'), - _('You must first cancel related internal picking attached to this manufacturing order.')) - if production.move_created_ids: - move_obj.action_cancel(cr, uid, [x.id for x in production.move_created_ids]) - move_obj.action_cancel(cr, uid, [x.id for x in production.move_lines]) - self.write(cr, uid, ids, {'state': 'cancel'}) - return True - - def action_ready(self, cr, uid, ids, context=None): - """ Changes the production state to Ready and location id of stock move. - @return: True - """ - move_obj = self.pool.get('stock.move') - self.write(cr, uid, ids, {'state': 'ready'}) - - for production in self.browse(cr, uid, ids, context=context): - if not production.move_created_ids: - produce_move_id = self._make_production_produce_line(cr, uid, production, context=context) - for scheduled in production.product_lines: - self._make_production_line_procurement(cr, uid, scheduled, False, context=context) - - if production.move_prod_id and production.move_prod_id.location_id.id != production.location_dest_id.id: - move_obj.write(cr, uid, [production.move_prod_id.id], - {'location_id': production.location_dest_id.id}) - return True - - def action_production_end(self, cr, uid, ids, context=None): - """ Changes production state to Finish and writes finished date. - @return: True - """ - for production in self.browse(cr, uid, ids): - self._costs_generate(cr, uid, production) - write_res = self.write(cr, uid, ids, {'state': 'done', 'date_finished': time.strftime('%Y-%m-%d %H:%M:%S')}) - return write_res - - def test_production_done(self, cr, uid, ids): - """ Tests whether production is done or not. - @return: True or False - """ - res = True - for production in self.browse(cr, uid, ids): - if production.move_lines: - res = False - - if production.move_created_ids: - res = False - return res - - def _get_subproduct_factor(self, cr, uid, production_id, move_id=None, context=None): - """ Compute the factor to compute the qty of procucts to produce for the given production_id. By default, - it's always equal to the quantity encoded in the production order or the production wizard, but if the - module mrp_subproduct is installed, then we must use the move_id to identify the product to produce - and its quantity. - :param production_id: ID of the mrp.order - :param move_id: ID of the stock move that needs to be produced. Will be used in mrp_subproduct. - :return: The factor to apply to the quantity that we should produce for the given production order. - """ - return 1 - - def action_produce(self, cr, uid, production_id, production_qty, production_mode, context=None): - """ To produce final product based on production mode (consume/consume&produce). - If Production mode is consume, all stock move lines of raw materials will be done/consumed. - If Production mode is consume & produce, all stock move lines of raw materials will be done/consumed - and stock move lines of final product will be also done/produced. - @param production_id: the ID of mrp.production object - @param production_qty: specify qty to produce - @param production_mode: specify production mode (consume/consume&produce). - @return: True - """ - stock_mov_obj = self.pool.get('stock.move') - production = self.browse(cr, uid, production_id, context=context) - - wf_service = netsvc.LocalService("workflow") - if not production.move_lines and production.state == 'ready': - # trigger workflow if not products to consume (eg: services) - wf_service.trg_validate(uid, 'mrp.production', production_id, 'button_produce', cr) - - produced_qty = 0 - for produced_product in production.move_created_ids2: - if (produced_product.scrapped) or (produced_product.product_id.id != production.product_id.id): - continue - produced_qty += produced_product.product_qty - if production_mode in ['consume','consume_produce']: - consumed_data = {} - - # Calculate already consumed qtys - for consumed in production.move_lines2: - if consumed.scrapped: - continue - if not consumed_data.get(consumed.product_id.id, False): - consumed_data[consumed.product_id.id] = 0 - consumed_data[consumed.product_id.id] += consumed.product_qty - - # Find product qty to be consumed and consume it - for scheduled in production.product_lines: - - # total qty of consumed product we need after this consumption - total_consume = ((production_qty + produced_qty) * scheduled.product_qty / production.product_qty) - - # qty available for consume and produce - qty_avail = scheduled.product_qty - consumed_data.get(scheduled.product_id.id, 0.0) - - if float_compare(qty_avail, 0, precision_rounding=scheduled.product_id.uom_id.rounding) <= 0: - # there will be nothing to consume for this raw material - continue - - raw_product = [move for move in production.move_lines if move.product_id.id==scheduled.product_id.id] - if raw_product: - # qtys we have to consume - qty = total_consume - consumed_data.get(scheduled.product_id.id, 0.0) - if float_compare(qty, qty_avail, precision_rounding=scheduled.product_id.uom_id.rounding) == 1: - # if qtys we have to consume is more than qtys available to consume - prod_name = scheduled.product_id.name_get()[0][1] - raise osv.except_osv(_('Warning!'), _('You are going to consume total %s quantities of "%s".\nBut you can only consume up to total %s quantities.') % (qty, prod_name, qty_avail)) - if float_compare(qty, 0, precision_rounding=scheduled.product_id.uom_id.rounding) <= 0: - # we already have more qtys consumed than we need - continue - - raw_product[0].action_consume(qty, raw_product[0].location_id.id, context=context) - production.refresh() - - if production_mode == 'consume_produce': - # To produce remaining qty of final product - #vals = {'state':'confirmed'} - #final_product_todo = [x.id for x in production.move_created_ids] - #stock_mov_obj.write(cr, uid, final_product_todo, vals) - #stock_mov_obj.action_confirm(cr, uid, final_product_todo, context) - produced_products = {} - for produced_product in production.move_created_ids2: - if produced_product.scrapped: - continue - if not produced_products.get(produced_product.product_id.id, False): - produced_products[produced_product.product_id.id] = 0 - produced_products[produced_product.product_id.id] += produced_product.product_qty - - for produce_product in production.move_created_ids: - produced_qty = produced_products.get(produce_product.product_id.id, 0) - subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context) - rest_qty = (subproduct_factor * production.product_qty) - produced_qty - - if rest_qty < (subproduct_factor * production_qty): - prod_name = produce_product.product_id.name_get()[0][1] - raise osv.except_osv(_('Warning!'), _('You are going to produce total %s quantities of "%s".\nBut you can only produce up to total %s quantities.') % ((subproduct_factor * production_qty), prod_name, rest_qty)) - if rest_qty > 0 : - stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), context=context) - production.refresh() - - for raw_product in production.move_lines2: - new_parent_ids = [] - parent_move_ids = [x.id for x in raw_product.move_history_ids] - for final_product in production.move_created_ids2: - if final_product.id not in parent_move_ids: - new_parent_ids.append(final_product.id) - for new_parent_id in new_parent_ids: - stock_mov_obj.write(cr, uid, [raw_product.id], {'move_history_ids': [(4,new_parent_id)]}) - - wf_service.trg_validate(uid, 'mrp.production', production_id, 'button_produce_done', cr) - return True - - def _costs_generate(self, cr, uid, production): - """ Calculates total costs at the end of the production. - @param production: Id of production order. - @return: Calculated amount. - """ - amount = 0.0 - analytic_line_obj = self.pool.get('account.analytic.line') - for wc_line in production.workcenter_lines: - wc = wc_line.workcenter_id - if wc.costs_journal_id and wc.costs_general_account_id: - # Cost per hour - value = wc_line.hour * wc.costs_hour - account = wc.costs_hour_account_id.id - if value and account: - amount += value - analytic_line_obj.create(cr, uid, { - 'name': wc_line.name + ' (H)', - 'amount': value, - 'account_id': account, - 'general_account_id': wc.costs_general_account_id.id, - 'journal_id': wc.costs_journal_id.id, - 'ref': wc.code, - 'product_id': wc.product_id.id, - 'unit_amount': wc_line.hour, - 'product_uom_id': wc.product_id and wc.product_id.uom_id.id or False - } ) - # Cost per cycle - value = wc_line.cycle * wc.costs_cycle - account = wc.costs_cycle_account_id.id - if value and account: - amount += value - analytic_line_obj.create(cr, uid, { - 'name': wc_line.name+' (C)', - 'amount': value, - 'account_id': account, - 'general_account_id': wc.costs_general_account_id.id, - 'journal_id': wc.costs_journal_id.id, - 'ref': wc.code, - 'product_id': wc.product_id.id, - 'unit_amount': wc_line.cycle, - 'product_uom_id': wc.product_id and wc.product_id.uom_id.id or False - } ) - return amount - - def action_in_production(self, cr, uid, ids, context=None): - """ Changes state to In Production and writes starting date. - @return: True - """ - return self.write(cr, uid, ids, {'state': 'in_production', 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')}) - - def test_if_product(self, cr, uid, ids): - """ - @return: True or False - """ - res = True - for production in self.browse(cr, uid, ids): - boms = self._prepare_lines(cr, uid, production)[0] - res = False - for bom in boms: - product = self.pool.get('product.product').browse(cr, uid, bom['product_id']) - if product.type in ('product', 'consu'): - res = True - return res - - def _get_auto_picking(self, cr, uid, production): - return True - - def _hook_create_post_procurement(self, cr, uid, production, procurement_id, context=None): - return True - - def _make_production_line_procurement(self, cr, uid, production_line, shipment_move_id, context=None): - wf_service = netsvc.LocalService("workflow") - procurement_order = self.pool.get('procurement.order') - production = production_line.production_id - location_id = production.location_src_id.id - date_planned = production.date_planned - procurement_name = (production.origin or '').split(':')[0] + ':' + production.name - procurement_id = procurement_order.create(cr, uid, { - 'name': procurement_name, - 'origin': procurement_name, - 'date_planned': date_planned, - 'product_id': production_line.product_id.id, - 'product_qty': production_line.product_qty, - 'product_uom': production_line.product_uom.id, - 'product_uos_qty': production_line.product_uos and production_line.product_qty or False, - 'product_uos': production_line.product_uos and production_line.product_uos.id or False, - 'location_id': location_id, - 'procure_method': production_line.product_id.procure_method, - 'move_id': shipment_move_id, - 'company_id': production.company_id.id, - }) - self._hook_create_post_procurement(cr, uid, production, procurement_id, context=context) - wf_service.trg_validate(uid, procurement_order._name, procurement_id, 'button_confirm', cr) - return procurement_id - - def _make_production_internal_shipment_line(self, cr, uid, production_line, shipment_id, parent_move_id, destination_location_id=False, context=None): - stock_move = self.pool.get('stock.move') - production = production_line.production_id - date_planned = production.date_planned - # Internal shipment is created for Stockable and Consumer Products - if production_line.product_id.type not in ('product', 'consu'): - return False - source_location_id = production.location_src_id.id - if not destination_location_id: - destination_location_id = source_location_id - return stock_move.create(cr, uid, { - 'name': production.name, - 'picking_id': shipment_id, - 'product_id': production_line.product_id.id, - 'product_qty': production_line.product_qty, - 'product_uom': production_line.product_uom.id, - 'product_uos_qty': production_line.product_uos and production_line.product_uos_qty or False, - 'product_uos': production_line.product_uos and production_line.product_uos.id or False, - 'date': date_planned, - 'move_dest_id': parent_move_id, - 'location_id': source_location_id, - 'location_dest_id': destination_location_id, - 'state': 'waiting', - 'company_id': production.company_id.id, - }) - - def _make_production_internal_shipment(self, cr, uid, production, context=None): - ir_sequence = self.pool.get('ir.sequence') - stock_picking = self.pool.get('stock.picking') - routing_loc = None - pick_type = 'internal' - partner_id = False - - # Take routing address as a Shipment Address. - # If usage of routing location is a internal, make outgoing shipment otherwise internal shipment - if production.bom_id.routing_id and production.bom_id.routing_id.location_id: - routing_loc = production.bom_id.routing_id.location_id - if routing_loc.usage != 'internal': - pick_type = 'out' - partner_id = routing_loc.partner_id and routing_loc.partner_id.id or False - - # Take next Sequence number of shipment base on type - if pick_type!='internal': - pick_name = ir_sequence.get(cr, uid, 'stock.picking.' + pick_type) - else: - pick_name = ir_sequence.get(cr, uid, 'stock.picking') - - picking_id = stock_picking.create(cr, uid, { - 'name': pick_name, - 'origin': (production.origin or '').split(':')[0] + ':' + production.name, - 'type': pick_type, - 'move_type': 'one', - 'state': 'auto', - 'partner_id': partner_id, - 'auto_picking': self._get_auto_picking(cr, uid, production), - 'company_id': production.company_id.id, - }) - production.write({'picking_id': picking_id}, context=context) - return picking_id - - def _make_production_produce_line(self, cr, uid, production, context=None): - stock_move = self.pool.get('stock.move') - source_location_id = production.product_id.property_stock_production.id - destination_location_id = production.location_dest_id.id - data = { - 'name': production.name, - 'date': production.date_planned, - 'product_id': production.product_id.id, - 'product_qty': production.product_qty, - 'product_uom': production.product_uom.id, - 'product_uos_qty': production.product_uos and production.product_uos_qty or False, - 'product_uos': production.product_uos and production.product_uos.id or False, - 'location_id': source_location_id, - 'location_dest_id': destination_location_id, - 'move_dest_id': production.move_prod_id.id, - 'state': 'waiting', - 'company_id': production.company_id.id, - } - if production.move_prod_id: - production.move_prod_id.write({'location_id': destination_location_id}) - move_id = stock_move.create(cr, uid, data, context=context) - production.write({'move_created_ids': [(6, 0, [move_id])]}, context=context) - return move_id - - def _make_production_consume_line(self, cr, uid, production_line, parent_move_id, source_location_id=False, context=None): - stock_move = self.pool.get('stock.move') - production = production_line.production_id - # Internal shipment is created for Stockable and Consumer Products - if production_line.product_id.type not in ('product', 'consu'): - return False - destination_location_id = production.product_id.property_stock_production.id - if not source_location_id: - source_location_id = production.location_src_id.id - move_id = stock_move.create(cr, uid, { - 'name': production.name, - 'date': production.date_planned, - 'product_id': production_line.product_id.id, - 'product_qty': production_line.product_qty, - 'product_uom': production_line.product_uom.id, - 'product_uos_qty': production_line.product_uos and production_line.product_uos_qty or False, - 'product_uos': production_line.product_uos and production_line.product_uos.id or False, - 'location_id': source_location_id, - 'location_dest_id': destination_location_id, - 'move_dest_id': parent_move_id, - 'state': 'waiting', - 'company_id': production.company_id.id, - }) - production.write({'move_lines': [(4, move_id)]}, context=context) - return move_id - - def action_confirm(self, cr, uid, ids, context=None): - """ Confirms production order. - @return: Newly generated Shipment Id. - """ - shipment_id = False - wf_service = netsvc.LocalService("workflow") - uncompute_ids = filter(lambda x:x, [not x.product_lines and x.id or False for x in self.browse(cr, uid, ids, context=context)]) - self.action_compute(cr, uid, uncompute_ids, context=context) - for production in self.browse(cr, uid, ids, context=context): - shipment_id = self._make_production_internal_shipment(cr, uid, production, context=context) - produce_move_id = self._make_production_produce_line(cr, uid, production, context=context) - - # Take routing location as a Source Location. - source_location_id = production.location_src_id.id - if production.routing_id and production.routing_id.location_id: - source_location_id = production.routing_id.location_id.id - - for line in production.product_lines: - consume_move_id = self._make_production_consume_line(cr, uid, line, produce_move_id, source_location_id=source_location_id, context=context) - if shipment_id: - shipment_move_id = self._make_production_internal_shipment_line(cr, uid, line, shipment_id, consume_move_id,\ - destination_location_id=source_location_id, context=context) - self._make_production_line_procurement(cr, uid, line, shipment_move_id, context=context) - - if shipment_id: - wf_service.trg_validate(uid, 'stock.picking', shipment_id, 'button_confirm', cr) - production.write({'state':'confirmed'}, context=context) - return shipment_id - - def force_production(self, cr, uid, ids, *args): - """ Assigns products. - @param *args: Arguments - @return: True - """ - pick_obj = self.pool.get('stock.picking') - pick_obj.force_assign(cr, uid, [prod.picking_id.id for prod in self.browse(cr, uid, ids)]) - return True - - -class mrp_production_workcenter_line(osv.osv): - _name = 'mrp.production.workcenter.line' - _description = 'Work Order' - _order = 'sequence' - _inherit = ['mail.thread'] - - _columns = { - 'name': fields.char('Work Order', size=64, required=True), - 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), - 'cycle': fields.float('Number of Cycles', digits=(16,2)), - 'hour': fields.float('Number of Hours', digits=(16,2)), - 'sequence': fields.integer('Sequence', required=True, help="Gives the sequence order when displaying a list of work orders."), - 'production_id': fields.many2one('mrp.production', 'Manufacturing Order', - track_visibility='onchange', select=True, ondelete='cascade', required=True), - } - _defaults = { - 'sequence': lambda *a: 1, - 'hour': lambda *a: 0, - 'cycle': lambda *a: 0, - } - -class mrp_production_product_line(osv.osv): - _name = 'mrp.production.product.line' - _description = 'Production Scheduled Product' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), - 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True), - 'product_uos_qty': fields.float('Product UOS Quantity'), - 'product_uos': fields.many2one('product.uom', 'Product UOS'), - 'production_id': fields.many2one('mrp.production', 'Production Order', select=True), - } - -class product_product(osv.osv): - _inherit = "product.product" - _columns = { - 'bom_ids': fields.one2many('mrp.bom', 'product_id', 'Bill of Materials'), - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from datetime import datetime +import openerp.addons.decimal_precision as dp +from openerp.osv import fields, osv, orm +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP +from openerp.tools import float_compare +from openerp.tools.translate import _ +from openerp import netsvc +from openerp import tools +from openerp import SUPERUSER_ID + +class mrp_workcenter(osv.osv): + _name = 'mrp.workcenter' + _description = 'Work Center' + _inherits = {'resource.resource': 'resource_id'} + _columns = {'note': fields.text('Description', help="Description of the Work Center. Explain here what's a cycle according to this Work Center."), + 'capacity_per_cycle': fields.float('Capacity per Cycle', help='Number of operations this Work Center can do in parallel. If this Work Center represents a team of 5 workers, the capacity per cycle is 5.'), + 'time_cycle': fields.float('Time for 1 cycle (hour)', help='Time in hours for doing one cycle.'), + 'time_start': fields.float('Time before prod.', help='Time in hours for the setup.'), + 'time_stop': fields.float('Time after prod.', help='Time in hours for the cleaning.'), + 'costs_hour': fields.float('Cost per hour', help='Specify Cost of Work Center per hour.'), + 'costs_hour_account_id': fields.many2one('account.analytic.account', 'Hour Account', domain=[('type', '!=', 'view')], help='Fill this only if you want automatic analytic accounting entries on production orders.'), + 'costs_cycle': fields.float('Cost per cycle', help='Specify Cost of Work Center per cycle.'), + 'costs_cycle_account_id': fields.many2one('account.analytic.account', 'Cycle Account', domain=[('type', '!=', 'view')], help='Fill this only if you want automatic analytic accounting entries on production orders.'), + 'costs_journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal'), + 'costs_general_account_id': fields.many2one('account.account', 'General Account', domain=[('type', '!=', 'view')]), + 'resource_id': fields.many2one('resource.resource', 'Resource', ondelete='cascade', required=True), + 'product_id': fields.many2one('product.product', 'Work Center Product', help='Fill this product to easily track your production costs in the analytic accounting.')} + _defaults = {'capacity_per_cycle': 1.0, + 'resource_type': 'material'} + + def on_change_product_cost(self, cr, uid, ids, product_id, context = None): + value = {} + if product_id: + cost = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + value = {'costs_hour': cost.standard_price} + return {'value': value} + + +mrp_workcenter() + +class mrp_routing(osv.osv): + """ + For specifying the routings of Work Centers. + """ + _name = 'mrp.routing' + _description = 'Routing' + _columns = {'name': fields.char('Name', size=64, required=True), + 'active': fields.boolean('Active', help='If the active field is set to False, it will allow you to hide the routing without removing it.'), + 'code': fields.char('Code', size=8), + 'note': fields.text('Description'), + 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Work Centers'), + 'location_id': fields.many2one('stock.location', 'Production Location', help='Keep empty if you produce at the location where the finished products are needed.Set a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations.'), + 'company_id': fields.many2one('res.company', 'Company')} + _defaults = {'active': lambda *a: 1, + 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.routing', context=context)} + + +mrp_routing() + +class mrp_routing_workcenter(osv.osv): + """ + Defines working cycles and hours of a Work Center using routings. + """ + _name = 'mrp.routing.workcenter' + _description = 'Work Center Usage' + _order = 'sequence' + _columns = {'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), + 'name': fields.char('Name', size=64, required=True), + 'sequence': fields.integer('Sequence', help='Gives the sequence order when displaying a list of routing Work Centers.'), + 'cycle_nbr': fields.float('Number of Cycles', required=True, help='Number of iterations this work center has to do in the specified operation of the routing.'), + 'hour_nbr': fields.float('Number of Hours', required=True, help='Time in hours for this Work Center to achieve the operation of the specified routing.'), + 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade', help='Routing indicates all the Work Centers used, for how long and/or cycles.If Routing is indicated then,the third tab of a production order (Work Centers) will be automatically pre-completed.'), + 'note': fields.text('Description'), + 'company_id': fields.related('routing_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True)} + _defaults = {'cycle_nbr': lambda *a: 1.0, + 'hour_nbr': lambda *a: 0.0} + + +mrp_routing_workcenter() + +class mrp_bom(osv.osv): + """ + Defines bills of material for a product. + """ + _name = 'mrp.bom' + _description = 'Bill of Material' + _inherit = ['mail.thread'] + + def _child_compute(self, cr, uid, ids, name, arg, context = None): + """ Gets child bom. + @param self: The object pointer + @param cr: The current row, from the database cursor, + @param uid: The current user ID for security checks + @param ids: List of selected IDs + @param name: Name of the field + @param arg: User defined argument + @param context: A standard dictionary for contextual values + @return: Dictionary of values + """ + result = {} + if context is None: + context = {} + bom_obj = self.pool.get('mrp.bom') + bom_id = context and context.get('active_id', False) or False + cr.execute('select id from mrp_bom') + if all((bom_id != r[0] for r in cr.fetchall())): + ids.sort() + bom_id = ids[0] + bom_parent = bom_obj.browse(cr, uid, bom_id, context=context) + for bom in self.browse(cr, uid, ids, context=context): + if bom_parent or bom.id == bom_id: + result[bom.id] = map(lambda x: x.id, bom.bom_lines) + else: + result[bom.id] = [] + if bom.bom_lines: + continue + ok = name == 'child_complete_ids' and bom.product_id.supply_method == 'produce' + if bom.type == 'phantom' or ok: + sids = bom_obj.search(cr, uid, [('bom_id', '=', False), ('product_id', '=', bom.product_id.id)]) + if sids: + bom2 = bom_obj.browse(cr, uid, sids[0], context=context) + result[bom.id] += map(lambda x: x.id, bom2.bom_lines) + + return result + + def _compute_type(self, cr, uid, ids, field_name, arg, context = None): + """ Sets particular method for the selected bom type. + @param field_name: Name of the field + @param arg: User defined argument + @return: Dictionary of values + """ + res = dict.fromkeys(ids, False) + for line in self.browse(cr, uid, ids, context=context): + if line.type == 'phantom' and not line.bom_id: + res[line.id] = 'set' + continue + if line.bom_lines or line.type == 'phantom': + continue + if line.product_id.supply_method == 'produce': + if line.product_id.procure_method == 'make_to_stock': + res[line.id] = 'stock' + else: + res[line.id] = 'order' + + return res + + _columns = {'name': fields.char('Name', size=64), + 'code': fields.char('Reference', size=16), + 'active': fields.boolean('Active', help='If the active field is set to False, it will allow you to hide the bills of material without removing it.'), + 'type': fields.selection([('normal', 'Normal BoM'), ('phantom', 'Sets / Phantom')], 'BoM Type', required=True, help="If a by-product is used in several products, it can be useful to create its own BoM. Though if you don't want separated production orders for this by-product, select Set/Phantom as BoM type. If a Phantom BoM is used for a root product, it will be sold and shipped as a set of components, instead of being produced."), + 'method': fields.function(_compute_type, string='Method', type='selection', selection=[('', ''), + ('stock', 'On Stock'), + ('order', 'On Order'), + ('set', 'Set / Pack')]), + 'date_start': fields.date('Valid From', help="Validity of this BoM or component. Keep empty if it's always valid."), + 'date_stop': fields.date('Valid Until', help="Validity of this BoM or component. Keep empty if it's always valid."), + 'sequence': fields.integer('Sequence', help='Gives the sequence order when displaying a list of bills of material.'), + 'position': fields.char('Internal Reference', size=64, help='Reference to a position in an external plan.'), + 'product_id': fields.many2one('product.product', 'Product', required=True), + 'product_uos_qty': fields.float('Product UOS Qty'), + 'product_uos': fields.many2one('product.uom', 'Product UOS', help='Product UOS (Unit of Sale) is the unit of measurement for the invoicing and promotion of stock.'), + 'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')), + 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, help='Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control'), + 'product_rounding': fields.float('Product Rounding', help='Rounding applied on the product quantity.'), + 'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help='A factor of 0.9 means a loss of 10% within the production process.'), + 'bom_lines': fields.one2many('mrp.bom', 'bom_id', 'BoM Lines'), + 'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True), + 'routing_id': fields.many2one('mrp.routing', 'Routing', help='The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning.'), + 'property_ids': fields.many2many('mrp.property', 'mrp_bom_property_rel', 'bom_id', 'property_id', 'Properties'), + 'child_complete_ids': fields.function(_child_compute, relation='mrp.bom', string='BoM Hierarchy', type='many2many'), + 'company_id': fields.many2one('res.company', 'Company', required=True)} + _defaults = {'active': lambda *a: 1, + 'product_efficiency': lambda *a: 1.0, + 'product_qty': lambda *a: 1.0, + 'product_rounding': lambda *a: 0.0, + 'type': lambda *a: 'normal', + 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.bom', context=c)} + _order = 'sequence' + _parent_name = 'bom_id' + _sql_constraints = [('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0.\nYou should install the mrp_byproduct module if you want to manage extra products on BoMs !')] + + def _check_recursion(self, cr, uid, ids, context = None): + level = 100 + while len(ids): + cr.execute('select distinct bom_id from mrp_bom where id IN %s', (tuple(ids),)) + ids = filter(None, map(lambda x: x[0], cr.fetchall())) + if not level: + return False + level -= 1 + + return True + + def _check_product(self, cr, uid, ids, context = None): + all_prod = [] + boms = self.browse(cr, uid, ids, context=context) + + def check_bom(boms): + res = True + for bom in boms: + if bom.product_id.id in all_prod: + res = res and False + all_prod.append(bom.product_id.id) + lines = bom.bom_lines + if lines: + res = res and check_bom([ bom_id for bom_id in lines if bom_id not in boms ]) + + return res + + return check_bom(boms) + + _constraints = [(_check_recursion, 'Error ! You cannot create recursive BoM.', ['parent_id']), (_check_product, 'BoM line product should not be same as BoM product.', ['product_id'])] + + def onchange_product_id(self, cr, uid, ids, product_id, name, context = None): + """ Changes UoM and name if product_id changes. + @param name: Name of the field + @param product_id: Changed product_id + @return: Dictionary of changed values + """ + if product_id: + prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + return {'value': {'name': prod.name, + 'product_uom': prod.uom_id.id}} + return {} + + def onchange_uom(self, cr, uid, ids, product_id, product_uom, context = None): + res = {'value': {}} + if not product_uom or not product_id: + return res + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), + 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} + res['value'].update({'product_uom': product.uom_id.id}) + return res + + def _bom_find(self, cr, uid, product_id, product_uom, properties = None): + """ Finds BoM for particular product and product uom. + @param product_id: Selected product. + @param product_uom: Unit of measure of a product. + @param properties: List of related properties. + @return: False or BoM id. + """ + if properties is None: + properties = [] + cr.execute('select id from mrp_bom where product_id=%s and bom_id is null order by sequence', (product_id,)) + ids = map(lambda x: x[0], cr.fetchall()) + max_prop = 0 + result = False + for bom in self.pool.get('mrp.bom').browse(cr, uid, ids): + prop = 0 + for prop_id in bom.property_ids: + if prop_id.id in properties: + prop += 1 + + if prop > max_prop or max_prop == 0 and not result: + result = bom.id + max_prop = prop + + return result + + def _bom_explode(self, cr, uid, bom, factor, properties = None, addthis = False, level = 0, routing_id = False): + """ Finds Products and Work Centers for related BoM for manufacturing order. + @param bom: BoM of particular product. + @param factor: Factor of product UoM. + @param properties: A List of properties Ids. + @param addthis: If BoM found then True else False. + @param level: Depth level to find BoM lines starts from 10. + @return: result: List of dictionaries containing product details. + result2: List of dictionaries containing Work Center details. + """ + routing_obj = self.pool.get('mrp.routing') + factor = factor / (bom.product_efficiency or 1.0) + max_rounding = max(bom.product_rounding, bom.product_uom.rounding) + factor = rounding(factor, max_rounding) + if factor < max_rounding: + factor = max_rounding + result = [] + result2 = [] + phantom = False + if bom.type == 'phantom' and not bom.bom_lines: + newbom = self._bom_find(cr, uid, bom.product_id.id, bom.product_uom.id, properties) + if newbom: + res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0], factor * bom.product_qty, properties, addthis=True, level=level + 10) + result = result + res[0] + result2 = result2 + res[1] + phantom = True + else: + phantom = False + if not phantom: + if addthis and not bom.bom_lines: + result.append({'name': bom.product_id.name, + 'product_id': bom.product_id.id, + 'product_qty': bom.product_qty * factor, + 'product_uom': bom.product_uom.id, + 'product_uos_qty': bom.product_uos and bom.product_uos_qty * factor or False, + 'product_uos': bom.product_uos and bom.product_uos.id or False}) + routing = routing_id and routing_obj.browse(cr, uid, routing_id) or bom.routing_id or False + if routing: + for wc_use in routing.workcenter_lines: + wc = wc_use.workcenter_id + d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle) + mult = d + (m and 1.0 or 0.0) + cycle = mult * wc_use.cycle_nbr + result2.append({'name': tools.ustr(wc_use.name) + ' - ' + tools.ustr(bom.product_id.name), + 'workcenter_id': wc.id, + 'sequence': level + (wc_use.sequence or 0), + 'cycle': cycle, + 'hour': float(wc_use.hour_nbr * mult + ((wc.time_start or 0.0) + (wc.time_stop or 0.0) + cycle * (wc.time_cycle or 0.0)) * (wc.time_efficiency or 1.0))}) + + for bom2 in bom.bom_lines: + res = self._bom_explode(cr, uid, bom2, factor, properties, addthis=True, level=level + 10) + result = result + res[0] + result2 = result2 + res[1] + + return (result, result2) + + def copy_data(self, cr, uid, id, default = None, context = None): + if default is None: + default = {} + bom_data = self.read(cr, uid, id, [], context=context) + default.update(name=_('%s (copy)') % bom_data['name'], bom_id=False) + return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context) + + +def rounding(f, r): + import math + if not r: + return f + return math.ceil(f / r) * r + + +class mrp_production(osv.osv): + """ + Production Orders / Manufacturing Orders + """ + _name = 'mrp.production' + _description = 'Manufacturing Order' + _date_name = 'date_planned' + _inherit = ['mail.thread', 'ir.needaction_mixin'] + + def _production_calc(self, cr, uid, ids, prop, unknow_none, context = None): + """ Calculates total hours and total no. of cycles for a production order. + @param prop: Name of field. + @param unknow_none: + @return: Dictionary of values. + """ + result = {} + for prod in self.browse(cr, uid, ids, context=context): + result[prod.id] = {'hour_total': 0.0, + 'cycle_total': 0.0} + for wc in prod.workcenter_lines: + result[prod.id]['hour_total'] += wc.hour + result[prod.id]['cycle_total'] += wc.cycle + + return result + + def _src_id_default(self, cr, uid, ids, context = None): + try: + location_model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') + self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) + except (orm.except_orm, ValueError): + location_id = False + + return location_id + + def _dest_id_default(self, cr, uid, ids, context = None): + try: + location_model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') + self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) + except (orm.except_orm, ValueError): + location_id = False + + return location_id + + _columns = {'name': fields.char('Reference', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}), + 'origin': fields.char('Source Document', size=64, readonly=True, states={'draft': [('readonly', False)]}, help='Reference of the document that generated this production order request.'), + 'priority': fields.selection([('0', 'Not urgent'), + ('1', 'Normal'), + ('2', 'Urgent'), + ('3', 'Very Urgent')], 'Priority', select=True, readonly=True, states=dict.fromkeys(['draft', 'confirmed'], [('readonly', False)])), + 'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True, states={'draft': [('readonly', False)]}), + 'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, readonly=True, states={'draft': [('readonly', False)]}), + 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}), + 'product_uos_qty': fields.float('Product UoS Quantity', readonly=True, states={'draft': [('readonly', False)]}), + 'product_uos': fields.many2one('product.uom', 'Product UoS', readonly=True, states={'draft': [('readonly', False)]}), + 'location_src_id': fields.many2one('stock.location', 'Raw Materials Location', required=True, readonly=True, states={'draft': [('readonly', False)]}, help='Location where the system will look for components.'), + 'location_dest_id': fields.many2one('stock.location', 'Finished Products Location', required=True, readonly=True, states={'draft': [('readonly', False)]}, help='Location where the system will stock the finished products.'), + 'date_planned': fields.datetime('Scheduled Date', required=True, select=1, readonly=True, states={'draft': [('readonly', False)]}), + 'date_start': fields.datetime('Start Date', select=True, readonly=True), + 'date_finished': fields.datetime('End Date', select=True, readonly=True), + 'bom_id': fields.many2one('mrp.bom', 'Bill of Material', domain=[('bom_id', '=', False)], readonly=True, states={'draft': [('readonly', False)]}, help='Bill of Materials allow you to define the list of required raw materials to make a finished product.'), + 'routing_id': fields.many2one('mrp.routing', string='Routing', on_delete='set null', readonly=True, states={'draft': [('readonly', False)]}, help='The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production plannification.'), + 'picking_id': fields.many2one('stock.picking', 'Picking List', readonly=True, ondelete='restrict', help='This is the Internal Picking List that brings the finished product to the production plan'), + 'move_prod_id': fields.many2one('stock.move', 'Product Move', readonly=True), + 'move_lines': fields.many2many('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Products to Consume', domain=[('state', 'not in', ('done', 'cancel'))], readonly=True, states={'draft': [('readonly', False)]}), + 'move_lines2': fields.many2many('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Consumed Products', domain=[('state', 'in', ('done', 'cancel'))], readonly=True, states={'draft': [('readonly', False)]}), + 'move_created_ids': fields.one2many('stock.move', 'production_id', 'Products to Produce', domain=[('state', 'not in', ('done', 'cancel'))], readonly=True, states={'draft': [('readonly', False)]}), + 'move_created_ids2': fields.one2many('stock.move', 'production_id', 'Produced Products', domain=[('state', 'in', ('done', 'cancel'))], readonly=True, states={'draft': [('readonly', False)]}), + 'product_lines': fields.one2many('mrp.production.product.line', 'production_id', 'Scheduled goods', readonly=True, states={'draft': [('readonly', False)]}), + 'workcenter_lines': fields.one2many('mrp.production.workcenter.line', 'production_id', 'Work Centers Utilisation', readonly=True, states={'draft': [('readonly', False)]}), + 'state': fields.selection([('draft', 'New'), + ('cancel', 'Cancelled'), + ('picking_except', 'Picking Exception'), + ('confirmed', 'Awaiting Raw Materials'), + ('ready', 'Ready to Produce'), + ('in_production', 'Production Started'), + ('done', 'Done')], string='Status', readonly=True, track_visibility='onchange', help="When the production order is created the status is set to 'Draft'.\n If the order is confirmed the status is set to 'Waiting Goods'.\n If any exceptions are there, the status is set to 'Picking Exception'.\n If the stock is available then the status is set to 'Ready to Produce'.\n When the production gets started then the status is set to 'In Production'.\n When the production is over, the status is set to 'Done'."), + 'hour_total': fields.function(_production_calc, type='float', string='Total Hours', multi='workorder', store=True), + 'cycle_total': fields.function(_production_calc, type='float', string='Total Cycles', multi='workorder', store=True), + 'user_id': fields.many2one('res.users', 'Responsible'), + 'company_id': fields.many2one('res.company', 'Company', required=True)} + _defaults = {'priority': lambda *a: '1', + 'state': lambda *a: 'draft', + 'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'product_qty': lambda *a: 1.0, + 'user_id': lambda self, cr, uid, c: uid, + 'name': lambda x, y, z, c: x.pool.get('ir.sequence').get(y, z, 'mrp.production') or '/', + 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.production', context=c), + 'location_src_id': _src_id_default, + 'location_dest_id': _dest_id_default} + _sql_constraints = [('name_uniq', 'unique(name, company_id)', 'Reference must be unique per Company!')] + _order = 'priority desc, date_planned asc' + + def _check_qty(self, cr, uid, ids, context = None): + for order in self.browse(cr, uid, ids, context=context): + if order.product_qty <= 0: + return False + + return True + + _constraints = [(_check_qty, 'Order quantity cannot be negative or zero!', ['product_qty'])] + + def unlink(self, cr, uid, ids, context = None): + for production in self.browse(cr, uid, ids, context=context): + if production.state not in ('draft', 'cancel'): + raise osv.except_osv(_('Invalid Action!'), _("Cannot delete a manufacturing order in state '%s'.") % production.state) + + return super(mrp_production, self).unlink(cr, uid, ids, context=context) + + def copy(self, cr, uid, id, default = None, context = None): + if default is None: + default = {} + default.update({'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'), + 'move_lines': [], + 'move_lines2': [], + 'move_created_ids': [], + 'move_created_ids2': [], + 'product_lines': [], + 'move_prod_id': False, + 'picking_id': False}) + return super(mrp_production, self).copy(cr, uid, id, default, context) + + def location_id_change(self, cr, uid, ids, src, dest, context = None): + """ Changes destination location if source location is changed. + @param src: Source location id. + @param dest: Destination location id. + @return: Dictionary of values. + """ + if dest: + return {} + if src: + return {'value': {'location_dest_id': src}} + return {} + + def product_id_change(self, cr, uid, ids, product_id, context = None): + """ Finds UoM of changed product. + @param product_id: Id of changed product. + @return: Dictionary of values. + """ + if not product_id: + return {'value': {'product_uom': False, + 'bom_id': False, + 'routing_id': False}} + bom_obj = self.pool.get('mrp.bom') + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + bom_id = bom_obj._bom_find(cr, uid, product.id, product.uom_id and product.uom_id.id, []) + routing_id = False + if bom_id: + bom_point = bom_obj.browse(cr, uid, bom_id, context=context) + routing_id = bom_point.routing_id.id or False + product_uom_id = product.uom_id and product.uom_id.id or False + result = {'product_uom': product_uom_id, + 'bom_id': bom_id, + 'routing_id': routing_id} + return {'value': result} + + def bom_id_change(self, cr, uid, ids, bom_id, context = None): + """ Finds routing for changed BoM. + @param product: Id of product. + @return: Dictionary of values. + """ + if not bom_id: + return {'value': {'routing_id': False}} + bom_point = self.pool.get('mrp.bom').browse(cr, uid, bom_id, context=context) + routing_id = bom_point.routing_id.id or False + result = {'routing_id': routing_id} + return {'value': result} + + def action_picking_except(self, cr, uid, ids): + """ Changes the state to Exception. + @return: True + """ + self.write(cr, uid, ids, {'state': 'picking_except'}) + return True + + def action_compute(self, cr, uid, ids, properties = None, context = None): + """ Computes bills of material of a product. + @param properties: List containing dictionaries of properties. + @return: No. of products. + """ + if properties is None: + properties = [] + results = [] + bom_obj = self.pool.get('mrp.bom') + uom_obj = self.pool.get('product.uom') + prod_line_obj = self.pool.get('mrp.production.product.line') + workcenter_line_obj = self.pool.get('mrp.production.workcenter.line') + for production in self.browse(cr, uid, ids): + p_ids = prod_line_obj.search(cr, SUPERUSER_ID, [('production_id', '=', production.id)], context=context) + prod_line_obj.unlink(cr, SUPERUSER_ID, p_ids, context=context) + w_ids = workcenter_line_obj.search(cr, SUPERUSER_ID, [('production_id', '=', production.id)], context=context) + workcenter_line_obj.unlink(cr, SUPERUSER_ID, w_ids, context=context) + bom_point = production.bom_id + bom_id = production.bom_id.id + if not bom_point: + bom_id = bom_obj._bom_find(cr, uid, production.product_id.id, production.product_uom.id, properties) + if bom_id: + bom_point = bom_obj.browse(cr, uid, bom_id) + routing_id = bom_point.routing_id.id or False + self.write(cr, uid, [production.id], {'bom_id': bom_id, + 'routing_id': routing_id}) + if not bom_id: + raise osv.except_osv(_('Error!'), _('Cannot find a bill of material for this product.')) + factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id) + res = bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id) + results = res[0] + results2 = res[1] + for line in results: + line['production_id'] = production.id + prod_line_obj.create(cr, uid, line) + + for line in results2: + line['production_id'] = production.id + workcenter_line_obj.create(cr, uid, line) + + return len(results) + + def action_cancel(self, cr, uid, ids, context = None): + """ Cancels the production order and related stock moves. + @return: True + """ + if context is None: + context = {} + move_obj = self.pool.get('stock.move') + for production in self.browse(cr, uid, ids, context=context): + if production.state == 'confirmed' and production.picking_id.state not in ('draft', 'cancel'): + raise osv.except_osv(_('Cannot cancel manufacturing order!'), _('You must first cancel related internal picking attached to this manufacturing order.')) + if production.move_created_ids: + move_obj.action_cancel(cr, uid, [ x.id for x in production.move_created_ids ]) + move_obj.action_cancel(cr, uid, [ x.id for x in production.move_lines ]) + + self.write(cr, uid, ids, {'state': 'cancel'}) + return True + + def action_ready(self, cr, uid, ids, context = None): + """ Changes the production state to Ready and location id of stock move. + @return: True + """ + move_obj = self.pool.get('stock.move') + self.write(cr, uid, ids, {'state': 'ready'}) + for production_id, name in self.name_get(cr, uid, ids): + production = self.browse(cr, uid, production_id) + if production.move_prod_id and production.move_prod_id.location_id.id != production.location_dest_id.id: + move_obj.write(cr, uid, [production.move_prod_id.id], {'location_id': production.location_dest_id.id}) + + return True + + def action_production_end(self, cr, uid, ids, context = None): + """ Changes production state to Finish and writes finished date. + @return: True + """ + for production in self.browse(cr, uid, ids): + self._costs_generate(cr, uid, production) + + write_res = self.write(cr, uid, ids, {'state': 'done', + 'date_finished': time.strftime('%Y-%m-%d %H:%M:%S')}) + return write_res + + def test_production_done(self, cr, uid, ids): + """ Tests whether production is done or not. + @return: True or False + """ + res = True + for production in self.browse(cr, uid, ids): + if production.move_lines: + res = False + if production.move_created_ids: + res = False + + return res + + def _get_subproduct_factor(self, cr, uid, production_id, move_id = None, context = None): + """ Compute the factor to compute the qty of procucts to produce for the given production_id. By default, + it's always equal to the quantity encoded in the production order or the production wizard, but if the + module mrp_subproduct is installed, then we must use the move_id to identify the product to produce + and its quantity. + :param production_id: ID of the mrp.order + :param move_id: ID of the stock move that needs to be produced. Will be used in mrp_subproduct. + :return: The factor to apply to the quantity that we should produce for the given production order. + """ + return 1 + + def action_produce(self, cr, uid, production_id, production_qty, production_mode, context = None): + """ To produce final product based on production mode (consume/consume&produce). + If Production mode is consume, all stock move lines of raw materials will be done/consumed. + If Production mode is consume & produce, all stock move lines of raw materials will be done/consumed + and stock move lines of final product will be also done/produced. + @param production_id: the ID of mrp.production object + @param production_qty: specify qty to produce + @param production_mode: specify production mode (consume/consume&produce). + @return: True + """ + stock_mov_obj = self.pool.get('stock.move') + production = self.browse(cr, uid, production_id, context=context) + produced_qty = 0 + for produced_product in production.move_created_ids2: + if produced_product.scrapped or produced_product.product_id.id != production.product_id.id: + continue + produced_qty += produced_product.product_qty + + if production_mode in ('consume', 'consume_produce'): + consumed_data = {} + for consumed in production.move_lines2: + if consumed.scrapped: + continue + if not consumed_data.get(consumed.product_id.id, False): + consumed_data[consumed.product_id.id] = 0 + consumed_data[consumed.product_id.id] += consumed.product_qty + + for scheduled in production.product_lines: + total_consume = (production_qty + produced_qty) * scheduled.product_qty / production.product_qty + qty_avail = scheduled.product_qty - consumed_data.get(scheduled.product_id.id, 0.0) + if qty_avail <= 0.0: + continue + raw_product = [ move for move in production.move_lines if move.product_id.id == scheduled.product_id.id ] + if raw_product: + qty = total_consume - consumed_data.get(scheduled.product_id.id, 0.0) + if float_compare(qty, qty_avail, precision_rounding=scheduled.product_id.uom_id.rounding) == 1: + prod_name = scheduled.product_id.name_get()[0][1] + raise osv.except_osv(_('Warning!'), _('You are going to consume total %s quantities of "%s".\nBut you can only consume up to total %s quantities.') % (qty, prod_name, qty_avail)) + if qty <= 0.0: + continue + raw_product[0].action_consume(qty, raw_product[0].location_id.id, context=context) + + if production_mode == 'consume_produce': + produced_products = {} + for produced_product in production.move_created_ids2: + if produced_product.scrapped: + continue + if not produced_products.get(produced_product.product_id.id, False): + produced_products[produced_product.product_id.id] = 0 + produced_products[produced_product.product_id.id] += produced_product.product_qty + + for produce_product in production.move_created_ids: + produced_qty = produced_products.get(produce_product.product_id.id, 0) + subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context) + rest_qty = subproduct_factor * production.product_qty - produced_qty + if rest_qty < production_qty: + prod_name = produce_product.product_id.name_get()[0][1] + raise osv.except_osv(_('Warning!'), _('You are going to produce total %s quantities of "%s".\nBut you can only produce up to total %s quantities.') % (production_qty, prod_name, rest_qty)) + if rest_qty > 0: + stock_mov_obj.action_consume(cr, uid, [produce_product.id], subproduct_factor * production_qty, context=context) + + for raw_product in production.move_lines2: + new_parent_ids = [] + parent_move_ids = [ x.id for x in raw_product.move_history_ids ] + for final_product in production.move_created_ids2: + if final_product.id not in parent_move_ids: + new_parent_ids.append(final_product.id) + + for new_parent_id in new_parent_ids: + stock_mov_obj.write(cr, uid, [raw_product.id], {'move_history_ids': [(4, new_parent_id)]}) + + wf_service = netsvc.LocalService('workflow') + wf_service.trg_validate(uid, 'mrp.production', production_id, 'button_produce_done', cr) + return True + + def _costs_generate(self, cr, uid, production): + """ Calculates total costs at the end of the production. + @param production: Id of production order. + @return: Calculated amount. + """ + amount = 0.0 + analytic_line_obj = self.pool.get('account.analytic.line') + for wc_line in production.workcenter_lines: + wc = wc_line.workcenter_id + if wc.costs_journal_id and wc.costs_general_account_id: + value = wc_line.hour * wc.costs_hour + account = wc.costs_hour_account_id.id + if value and account: + amount += value + analytic_line_obj.create(cr, uid, {'name': wc_line.name + ' (H)', + 'amount': value, + 'account_id': account, + 'general_account_id': wc.costs_general_account_id.id, + 'journal_id': wc.costs_journal_id.id, + 'ref': wc.code, + 'product_id': wc.product_id.id, + 'unit_amount': wc_line.hour, + 'product_uom_id': wc.product_id and wc.product_id.uom_id.id or False}) + value = wc_line.cycle * wc.costs_cycle + account = wc.costs_cycle_account_id.id + if value and account: + amount += value + analytic_line_obj.create(cr, uid, {'name': wc_line.name + ' (C)', + 'amount': value, + 'account_id': account, + 'general_account_id': wc.costs_general_account_id.id, + 'journal_id': wc.costs_journal_id.id, + 'ref': wc.code, + 'product_id': wc.product_id.id, + 'unit_amount': wc_line.cycle, + 'product_uom_id': wc.product_id and wc.product_id.uom_id.id or False}) + + return amount + + def action_in_production(self, cr, uid, ids, context = None): + """ Changes state to In Production and writes starting date. + @return: True + """ + return self.write(cr, uid, ids, {'state': 'in_production', + 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')}) + + def test_if_product(self, cr, uid, ids): + """ + @return: True or False + """ + res = True + for production in self.browse(cr, uid, ids): + if not production.product_lines: + if not self.action_compute(cr, uid, [production.id]): + res = False + + return res + + def _get_auto_picking(self, cr, uid, production): + return True + + def _make_production_line_procurement(self, cr, uid, production_line, shipment_move_id, context = None): + wf_service = netsvc.LocalService('workflow') + procurement_order = self.pool.get('procurement.order') + production = production_line.production_id + location_id = production.location_src_id.id + date_planned = production.date_planned + procurement_name = (production.origin or '').split(':')[0] + ':' + production.name + procurement_id = procurement_order.create(cr, uid, {'name': procurement_name, + 'origin': procurement_name, + 'date_planned': date_planned, + 'product_id': production_line.product_id.id, + 'product_qty': production_line.product_qty, + 'product_uom': production_line.product_uom.id, + 'product_uos_qty': production_line.product_uos and production_line.product_qty or False, + 'product_uos': production_line.product_uos and production_line.product_uos.id or False, + 'location_id': location_id, + 'procure_method': production_line.product_id.procure_method, + 'move_id': shipment_move_id, + 'company_id': production.company_id.id}) + wf_service.trg_validate(uid, procurement_order._name, procurement_id, 'button_confirm', cr) + return procurement_id + + def _make_production_internal_shipment_line(self, cr, uid, production_line, shipment_id, parent_move_id, destination_location_id = False, context = None): + stock_move = self.pool.get('stock.move') + production = production_line.production_id + date_planned = production.date_planned + if production_line.product_id.type not in ('product', 'consu'): + return False + source_location_id = production.location_src_id.id + if not destination_location_id: + destination_location_id = source_location_id + return stock_move.create(cr, uid, {'name': production.name, + 'picking_id': shipment_id, + 'product_id': production_line.product_id.id, + 'product_qty': production_line.product_qty, + 'product_uom': production_line.product_uom.id, + 'product_uos_qty': production_line.product_uos and production_line.product_uos_qty or False, + 'product_uos': production_line.product_uos and production_line.product_uos.id or False, + 'date': date_planned, + 'move_dest_id': parent_move_id, + 'location_id': source_location_id, + 'location_dest_id': destination_location_id, + 'state': 'waiting', + 'company_id': production.company_id.id}) + + def _make_production_internal_shipment(self, cr, uid, production, context = None): + ir_sequence = self.pool.get('ir.sequence') + stock_picking = self.pool.get('stock.picking') + routing_loc = None + pick_type = 'internal' + partner_id = False + if production.bom_id.routing_id and production.bom_id.routing_id.location_id: + routing_loc = production.bom_id.routing_id.location_id + if routing_loc.usage != 'internal': + pick_type = 'out' + partner_id = routing_loc.partner_id and routing_loc.partner_id.id or False + pick_name = ir_sequence.get(cr, uid, 'stock.picking.' + pick_type) + picking_id = stock_picking.create(cr, uid, {'name': pick_name, + 'origin': (production.origin or '').split(':')[0] + ':' + production.name, + 'type': pick_type, + 'move_type': 'one', + 'state': 'auto', + 'partner_id': partner_id, + 'auto_picking': self._get_auto_picking(cr, uid, production), + 'company_id': production.company_id.id}) + production.write({'picking_id': picking_id}, context=context) + return picking_id + + def _make_production_produce_line(self, cr, uid, production, context = None): + stock_move = self.pool.get('stock.move') + source_location_id = production.product_id.property_stock_production.id + destination_location_id = production.location_dest_id.id + data = {'name': production.name, + 'date': production.date_planned, + 'product_id': production.product_id.id, + 'product_qty': production.product_qty, + 'product_uom': production.product_uom.id, + 'product_uos_qty': production.product_uos and production.product_uos_qty or False, + 'product_uos': production.product_uos and production.product_uos.id or False, + 'location_id': source_location_id, + 'location_dest_id': destination_location_id, + 'move_dest_id': production.move_prod_id.id, + 'state': 'waiting', + 'company_id': production.company_id.id} + move_id = stock_move.create(cr, uid, data, context=context) + production.write({'move_created_ids': [(6, 0, [move_id])]}, context=context) + return move_id + + def _make_production_consume_line(self, cr, uid, production_line, parent_move_id, source_location_id = False, context = None): + stock_move = self.pool.get('stock.move') + production = production_line.production_id + if production_line.product_id.type not in ('product', 'consu'): + return False + destination_location_id = production.product_id.property_stock_production.id + if not source_location_id: + source_location_id = production.location_src_id.id + move_id = stock_move.create(cr, uid, {'name': production.name, + 'date': production.date_planned, + 'product_id': production_line.product_id.id, + 'product_qty': production_line.product_qty, + 'product_uom': production_line.product_uom.id, + 'product_uos_qty': production_line.product_uos and production_line.product_uos_qty or False, + 'product_uos': production_line.product_uos and production_line.product_uos.id or False, + 'location_id': source_location_id, + 'location_dest_id': destination_location_id, + 'move_dest_id': parent_move_id, + 'state': 'waiting', + 'company_id': production.company_id.id}) + production.write({'move_lines': [(4, move_id)]}, context=context) + return move_id + + def action_confirm(self, cr, uid, ids, context = None): + """ Confirms production order. + @return: Newly generated Shipment Id. + """ + shipment_id = False + wf_service = netsvc.LocalService('workflow') + uncompute_ids = filter(lambda x: x, [ not x.product_lines and x.id or False for x in self.browse(cr, uid, ids, context=context) ]) + self.action_compute(cr, uid, uncompute_ids, context=context) + for production in self.browse(cr, uid, ids, context=context): + shipment_id = self._make_production_internal_shipment(cr, uid, production, context=context) + produce_move_id = self._make_production_produce_line(cr, uid, production, context=context) + source_location_id = production.location_src_id.id + if production.bom_id.routing_id and production.bom_id.routing_id.location_id: + source_location_id = production.bom_id.routing_id.location_id.id + for line in production.product_lines: + consume_move_id = self._make_production_consume_line(cr, uid, line, produce_move_id, source_location_id=source_location_id, context=context) + shipment_move_id = self._make_production_internal_shipment_line(cr, uid, line, shipment_id, consume_move_id, destination_location_id=source_location_id, context=context) + self._make_production_line_procurement(cr, uid, line, shipment_move_id, context=context) + + wf_service.trg_validate(uid, 'stock.picking', shipment_id, 'button_confirm', cr) + production.write({'state': 'confirmed'}, context=context) + + return shipment_id + + def force_production(self, cr, uid, ids, *args): + """ Assigns products. + @param *args: Arguments + @return: True + """ + pick_obj = self.pool.get('stock.picking') + pick_obj.force_assign(cr, uid, [ prod.picking_id.id for prod in self.browse(cr, uid, ids) ]) + return True + + +class mrp_production_workcenter_line(osv.osv): + _name = 'mrp.production.workcenter.line' + _description = 'Work Order' + _order = 'sequence' + _inherit = ['mail.thread'] + _columns = {'name': fields.char('Work Order', size=64, required=True), + 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), + 'cycle': fields.float('Number of Cycles', digits=(16, 2)), + 'hour': fields.float('Number of Hours', digits=(16, 2)), + 'sequence': fields.integer('Sequence', required=True, help='Gives the sequence order when displaying a list of work orders.'), + 'production_id': fields.many2one('mrp.production', 'Manufacturing Order', track_visibility='onchange', select=True, ondelete='cascade', required=True)} + _defaults = {'sequence': lambda *a: 1, + 'hour': lambda *a: 0, + 'cycle': lambda *a: 0} + + +class mrp_production_product_line(osv.osv): + _name = 'mrp.production.product.line' + _description = 'Production Scheduled Product' + _columns = {'name': fields.char('Name', size=64, required=True), + 'product_id': fields.many2one('product.product', 'Product', required=True), + 'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), + 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True), + 'product_uos_qty': fields.float('Product UOS Quantity'), + 'product_uos': fields.many2one('product.uom', 'Product UOS'), + 'production_id': fields.many2one('mrp.production', 'Production Order', select=True)} + + +class product_product(osv.osv): + _inherit = 'product.product' + _columns = {'bom_ids': fields.one2many('mrp.bom', 'product_id', 'Bill of Materials')} \ No newline at end of file diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index 40d608b2a0f97..3530888850426 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -389,7 +389,7 @@ - + @@ -404,8 +404,8 @@ - - + +
    @@ -765,7 +765,7 @@ - + @@ -773,7 +773,7 @@ - + diff --git a/addons/mrp/procurement.py b/addons/mrp/procurement.py index 0897aa4fd0fbd..b10fadbf4e9c4 100644 --- a/addons/mrp/procurement.py +++ b/addons/mrp/procurement.py @@ -1,132 +1,126 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from datetime import datetime -from dateutil.relativedelta import relativedelta -from openerp.osv import fields -from openerp.osv import osv -from openerp.tools.translate import _ -from openerp import netsvc -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT - - -class procurement_order(osv.osv): - _inherit = 'procurement.order' - _columns = { - 'bom_id': fields.many2one('mrp.bom', 'BoM', ondelete='cascade', select=True), - 'property_ids': fields.many2many('mrp.property', 'procurement_property_rel', 'procurement_id','property_id', 'Properties'), - 'production_id': fields.many2one('mrp.production', 'Manufacturing Order'), - } - - def check_produce_product(self, cr, uid, procurement, context=None): - ''' Depict the capacity of the procurement workflow to produce products (not services)''' - return True - - def check_bom_exists(self, cr, uid, ids, context=None): - """ Finds the bill of material for the product from procurement order. - @return: True or False - """ - for procurement in self.browse(cr, uid, ids, context=context): - product = procurement.product_id - properties = [x.id for x in procurement.property_ids] - bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties) - if not bom_id: - cr.execute('update procurement_order set message=%s where id=%s', (_('No BoM defined for this product !'), procurement.id)) - for (id, name) in self.name_get(cr, uid, procurement.id): - message = _("Procurement '%s' has an exception: 'No BoM defined for this product !'") % name - self.message_post(cr, uid, [procurement.id], body=message, context=context) - return False - return True - - def check_conditions_confirm2wait(self, cr, uid, ids): - """ condition on the transition to go from 'confirm' activity to 'confirm_wait' activity """ - res = super(procurement_order, self).check_conditions_confirm2wait(cr, uid, ids) - return res and not self.get_phantom_bom_id(cr, uid, ids) - - def get_phantom_bom_id(self, cr, uid, ids, context=None): - for procurement in self.browse(cr, uid, ids, context=context): - if procurement.move_id and procurement.move_id.product_id.supply_method=='produce' \ - and procurement.move_id.product_id.procure_method=='make_to_order': - phantom_bom_id = self.pool.get('mrp.bom').search(cr, uid, [ - ('product_id', '=', procurement.move_id.product_id.id), - ('bom_id', '=', False), - ('type', '=', 'phantom')]) - return phantom_bom_id - return False - - def action_produce_assign_product(self, cr, uid, ids, context=None): - """ This is action which call from workflow to assign production order to procurements - @return: True - """ - procurement_obj = self.pool.get('procurement.order') - res = procurement_obj.make_mo(cr, uid, ids, context=context) - res = res.values() - return len(res) and res[0] or 0 - - def _get_date_planned(self, cr, uid, procurement, context=None): - format_date_planned = datetime.strptime(procurement.date_planned, - DEFAULT_SERVER_DATETIME_FORMAT) - date_planned = format_date_planned - relativedelta(days=procurement.product_id.produce_delay or 0.0) - date_planned = date_planned - relativedelta(days=procurement.company_id.manufacturing_lead) - return date_planned - - def _prepare_mo_vals(self, cr, uid, procurement, context=None): - res_id = procurement.move_id.id - newdate = self._get_date_planned(cr, uid, procurement, context=context) - return { - 'origin': procurement.origin, - 'product_id': procurement.product_id.id, - 'product_qty': procurement.product_qty, - 'product_uom': procurement.product_uom.id, - 'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False, - 'product_uos': procurement.product_uos and procurement.product_uos.id or False, - 'location_src_id': procurement.location_id.id, - 'location_dest_id': procurement.location_id.id, - 'bom_id': procurement.bom_id and procurement.bom_id.id or False, - 'date_planned': newdate.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'move_prod_id': res_id, - 'company_id': procurement.company_id.id, - } - - def make_mo(self, cr, uid, ids, context=None): - """ Make Manufacturing(production) order from procurement - @return: New created Production Orders procurement wise - """ - res = {} - production_obj = self.pool.get('mrp.production') - move_obj = self.pool.get('stock.move') - wf_service = netsvc.LocalService("workflow") - procurement_obj = self.pool.get('procurement.order') - for procurement in procurement_obj.browse(cr, uid, ids, context=context): - vals = self._prepare_mo_vals(cr, uid, procurement, context=context) - produce_id = production_obj.create(cr, uid, vals, context=context) - res[procurement.id] = produce_id - self.write(cr, uid, [procurement.id], {'state': 'running', 'production_id': produce_id}) - bom_result = production_obj.action_compute(cr, uid, - [produce_id], properties=[x.id for x in procurement.property_ids]) - wf_service.trg_validate(uid, 'mrp.production', produce_id, 'button_confirm', cr) - self.production_order_create_note(cr, uid, ids, context=context) - return res - - def production_order_create_note(self, cr, uid, ids, context=None): - for procurement in self.browse(cr, uid, ids, context=context): - body = _("Manufacturing Order %s created.") % ( procurement.production_id.name,) - self.message_post(cr, uid, [procurement.id], body=body, context=context) +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from datetime import datetime +from dateutil.relativedelta import relativedelta +from openerp.osv import fields +from openerp.osv import osv +from openerp.tools.translate import _ +from openerp import netsvc + +class procurement_order(osv.osv): + _inherit = 'procurement.order' + _columns = {'bom_id': fields.many2one('mrp.bom', 'BoM', ondelete='cascade', select=True), + 'property_ids': fields.many2many('mrp.property', 'procurement_property_rel', 'procurement_id', 'property_id', 'Properties'), + 'production_id': fields.many2one('mrp.production', 'Manufacturing Order')} + + def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, context = None): + result = super(procurement_order, self)._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, context) + result['property_ids'] = [(6, 0, [ x.id for x in line.property_ids ])] + return result + + def check_produce_product(self, cr, uid, procurement, context = None): + """ Depict the capacity of the procurement workflow to produce products (not services)""" + return True + + def check_bom_exists(self, cr, uid, ids, context = None): + """ Finds the bill of material for the product from procurement order. + @return: True or False + """ + for procurement in self.browse(cr, uid, ids, context=context): + product = procurement.product_id + properties = [ x.id for x in procurement.property_ids ] + bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties) + if not bom_id: + cr.execute('update procurement_order set message=%s where id=%s', (_('No BoM defined for this product !'), procurement.id)) + for id, name in self.name_get(cr, uid, procurement.id): + message = _("Procurement '%s' has an exception: 'No BoM defined for this product !'") % name + self.message_post(cr, uid, [procurement.id], body=message, context=context) + + return False + + return True + + def check_conditions_confirm2wait(self, cr, uid, ids): + """ condition on the transition to go from 'confirm' activity to 'confirm_wait' activity """ + res = super(procurement_order, self).check_conditions_confirm2wait(cr, uid, ids) + return res and not self.get_phantom_bom_id(cr, uid, ids) + + def get_phantom_bom_id(self, cr, uid, ids, context = None): + for procurement in self.browse(cr, uid, ids, context=context): + if procurement.move_id and procurement.move_id.product_id.supply_method == 'produce' and procurement.move_id.product_id.procure_method == 'make_to_order': + phantom_bom_id = self.pool.get('mrp.bom').search(cr, uid, [('product_id', '=', procurement.move_id.product_id.id), ('bom_id', '=', False), ('type', '=', 'phantom')]) + return phantom_bom_id + + return False + + def action_produce_assign_product(self, cr, uid, ids, context = None): + """ This is action which call from workflow to assign production order to procurements + @return: True + """ + procurement_obj = self.pool.get('procurement.order') + res = procurement_obj.make_mo(cr, uid, ids, context=context) + res = res.values() + return len(res) and res[0] or 0 + + def make_mo(self, cr, uid, ids, context = None): + """ Make Manufacturing(production) order from procurement + @return: New created Production Orders procurement wise + """ + res = {} + company = self.pool.get('res.users').browse(cr, uid, uid, context).company_id + production_obj = self.pool.get('mrp.production') + move_obj = self.pool.get('stock.move') + wf_service = netsvc.LocalService('workflow') + procurement_obj = self.pool.get('procurement.order') + for procurement in procurement_obj.browse(cr, uid, ids, context=context): + res_id = procurement.move_id.id + newdate = datetime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S') - relativedelta(days=procurement.product_id.produce_delay or 0.0) + newdate = newdate - relativedelta(days=company.manufacturing_lead) + produce_id = production_obj.create(cr, uid, {'origin': procurement.origin, + 'product_id': procurement.product_id.id, + 'product_qty': procurement.product_qty, + 'product_uom': procurement.product_uom.id, + 'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False, + 'product_uos': procurement.product_uos and procurement.product_uos.id or False, + 'location_src_id': procurement.location_id.id, + 'location_dest_id': procurement.location_id.id, + 'bom_id': procurement.bom_id and procurement.bom_id.id or False, + 'date_planned': newdate.strftime('%Y-%m-%d %H:%M:%S'), + 'move_prod_id': res_id, + 'company_id': procurement.company_id.id}) + res[procurement.id] = produce_id + self.write(cr, uid, [procurement.id], {'state': 'running', + 'production_id': produce_id}) + bom_result = production_obj.action_compute(cr, uid, [produce_id], properties=[ x.id for x in procurement.property_ids ]) + wf_service.trg_validate(uid, 'mrp.production', produce_id, 'button_confirm', cr) + if res_id: + move_obj.write(cr, uid, [res_id], {'location_id': procurement.location_id.id}) + + self.production_order_create_note(cr, uid, ids, context=context) + return res + + def production_order_create_note(self, cr, uid, ids, context = None): + for procurement in self.browse(cr, uid, ids, context=context): + body = _('Manufacturing Order %s created.') % (procurement.production_id.name,) + self.message_post(cr, uid, [procurement.id], body=body, context=context) + + +procurement_order() \ No newline at end of file diff --git a/addons/mrp/product.py b/addons/mrp/product.py index f5e29a1d103d2..cbadf6136a7a8 100644 --- a/addons/mrp/product.py +++ b/addons/mrp/product.py @@ -1,42 +1,35 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -from openerp.tools.translate import _ - - -class product_product(osv.osv): - _inherit = "product.product" - _columns = { - "bom_ids": fields.one2many('mrp.bom', 'product_id','Bill of Materials', domain=[('bom_id','=',False)]), - } - def copy(self, cr, uid, id, default=None, context=None): - if not default: - default = {} - default.update({ - 'bom_ids': [] - }) - return super(product_product, self).copy(cr, uid, id, default, context=context) - - -product_product() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +from openerp.tools.translate import _ + +class product_product(osv.osv): + _inherit = 'product.product' + _columns = {'bom_ids': fields.one2many('mrp.bom', 'product_id', 'Bill of Materials', domain=[('bom_id', '=', False)])} + + def copy(self, cr, uid, id, default = None, context = None): + if not default: + default = {} + default.update({'bom_ids': []}) + return super(product_product, self).copy(cr, uid, id, default, context=context) + + +product_product() \ No newline at end of file diff --git a/addons/mrp/report/bom_structure.py b/addons/mrp/report/bom_structure.py index ae26bc4f8cdc5..f89b5d4901b56 100644 --- a/addons/mrp/report/bom_structure.py +++ b/addons/mrp/report/bom_structure.py @@ -1,64 +1,60 @@ -## -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time -from openerp.report import report_sxw -from openerp.osv import osv -from openerp import pooler - -class bom_structure(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(bom_structure, self).__init__(cr, uid, name, context=context) - self.localcontext.update({ - 'time': time, - 'get_children':self.get_children, - }) - - def get_children(self, object, level=0): - result = [] - - def _get_rec(object, level): - for l in object: - res = {} - res['name'] = l.name - res['pname'] = l.product_id.name - res['pcode'] = l.product_id.default_code - res['pqty'] = l.product_qty - res['uname'] = l.product_uom.name - res['code'] = l.code - res['level'] = level - result.append(res) - if l.child_complete_ids: - if level<6: - level += 1 - _get_rec(l.child_complete_ids,level) - if level>0 and level<6: - level -= 1 - return result - - children = _get_rec(object,level) - - return children - -report_sxw.report_sxw('report.bom.structure','mrp.bom','mrp/report/bom_structure.rml',parser=bom_structure,header='internal') - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from openerp.report import report_sxw +from openerp.osv import osv +from openerp import pooler + +class bom_structure(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(bom_structure, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time, + 'get_children': self.get_children}) + + def get_children(self, object, level = 0): + result = [] + + def _get_rec(object, level): + for l in object: + res = {} + res['name'] = l.name + res['pname'] = l.product_id.name + res['pcode'] = l.product_id.default_code + res['pqty'] = l.product_qty + res['uname'] = l.product_uom.name + res['code'] = l.code + res['level'] = level + result.append(res) + if l.child_complete_ids: + if level < 6: + level += 1 + _get_rec(l.child_complete_ids, level) + if level > 0 and level < 6: + level -= 1 + + return result + + children = _get_rec(object, level) + return children + + +report_sxw.report_sxw('report.bom.structure', 'mrp.bom', 'mrp/report/bom_structure.rml', parser=bom_structure, header='internal') \ No newline at end of file diff --git a/addons/mrp/report/mrp_production_order.py b/addons/mrp/report/mrp_production_order.py index d0ff7ba7970c6..485b69a3a1ac2 100644 --- a/addons/mrp/report/mrp_production_order.py +++ b/addons/mrp/report/mrp_production_order.py @@ -1,24 +1,21 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +pass \ No newline at end of file diff --git a/addons/mrp/report/mrp_report.py b/addons/mrp/report/mrp_report.py index 7dff886a4baf1..5615f3b9be2e4 100644 --- a/addons/mrp/report/mrp_report.py +++ b/addons/mrp/report/mrp_report.py @@ -1,104 +1,52 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields,osv - - -class report_workcenter_load(osv.osv): - _name="report.workcenter.load" - _description="Work Center Load" - _auto = False - _log_access = False - _columns = { - 'name': fields.char('Week', size=64, required=True), - 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), - 'cycle': fields.float('Number of Cycles'), - 'hour': fields.float('Number of Hours'), - } - - def init(self, cr): - cr.execute(""" - create or replace view report_workcenter_load as ( - SELECT - min(wl.id) as id, - to_char(p.date_planned,'YYYY:mm:dd') as name, - SUM(wl.hour) AS hour, - SUM(wl.cycle) AS cycle, - wl.workcenter_id as workcenter_id - FROM - mrp_production_workcenter_line wl - LEFT JOIN mrp_production p - ON p.id = wl.production_id - GROUP BY - wl.workcenter_id, - to_char(p.date_planned,'YYYY:mm:dd') - )""") - -report_workcenter_load() - - -class report_mrp_inout(osv.osv): - _name="report.mrp.inout" - _description="Stock value variation" - _auto = False - _log_access = False - _rec_name = 'date' - _columns = { - 'date': fields.char('Week', size=64, required=True), - 'value': fields.float('Stock value', required=True, digits=(16,2)), - } - - def init(self, cr): - cr.execute(""" - create or replace view report_mrp_inout as ( - select - min(sm.id) as id, - to_char(sm.date,'YYYY:IW') as date, - sum(case when (sl.usage='internal') then - pt.standard_price * sm.product_qty - else - 0.0 - end - case when (sl2.usage='internal') then - pt.standard_price * sm.product_qty - else - 0.0 - end) as value - from - stock_move sm - left join product_product pp - on (pp.id = sm.product_id) - left join product_template pt - on (pt.id = pp.product_tmpl_id) - left join stock_location sl - on ( sl.id = sm.location_id) - left join stock_location sl2 - on ( sl2.id = sm.location_dest_id) - where - sm.state in ('waiting','confirmed','assigned') - group by - to_char(sm.date,'YYYY:IW') - )""") - -report_mrp_inout() - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv + +class report_workcenter_load(osv.osv): + _name = 'report.workcenter.load' + _description = 'Work Center Load' + _auto = False + _log_access = False + _columns = {'name': fields.char('Week', size=64, required=True), + 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), + 'cycle': fields.float('Number of Cycles'), + 'hour': fields.float('Number of Hours')} + + def init(self, cr): + cr.execute("\n create or replace view report_workcenter_load as (\n SELECT\n min(wl.id) as id,\n to_char(p.date_planned,'YYYY:mm:dd') as name,\n SUM(wl.hour) AS hour,\n SUM(wl.cycle) AS cycle,\n wl.workcenter_id as workcenter_id\n FROM\n mrp_production_workcenter_line wl\n LEFT JOIN mrp_production p\n ON p.id = wl.production_id\n GROUP BY\n wl.workcenter_id,\n to_char(p.date_planned,'YYYY:mm:dd')\n )") + + +report_workcenter_load() + +class report_mrp_inout(osv.osv): + _name = 'report.mrp.inout' + _description = 'Stock value variation' + _auto = False + _log_access = False + _rec_name = 'date' + _columns = {'date': fields.char('Week', size=64, required=True), + 'value': fields.float('Stock value', required=True, digits=(16, 2))} + + def init(self, cr): + cr.execute("\n create or replace view report_mrp_inout as (\n select\n min(sm.id) as id,\n to_char(sm.date,'YYYY:IW') as date,\n sum(case when (sl.usage='internal') then\n pt.standard_price * sm.product_qty\n else\n 0.0\n end - case when (sl2.usage='internal') then\n pt.standard_price * sm.product_qty\n else\n 0.0\n end) as value\n from\n stock_move sm\n left join product_product pp\n on (pp.id = sm.product_id)\n left join product_template pt\n on (pt.id = pp.product_tmpl_id)\n left join stock_location sl\n on ( sl.id = sm.location_id)\n left join stock_location sl2\n on ( sl2.id = sm.location_dest_id)\n where\n sm.state in ('waiting','confirmed','assigned')\n group by\n to_char(sm.date,'YYYY:IW')\n )") + + +report_mrp_inout() \ No newline at end of file diff --git a/addons/mrp/report/order.py b/addons/mrp/report/order.py index eb3f566367754..838aa61491f96 100644 --- a/addons/mrp/report/order.py +++ b/addons/mrp/report/order.py @@ -1,34 +1,31 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time -from openerp.report import report_sxw - -class order(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(order, self).__init__(cr, uid, name, context=context) - self.localcontext.update({ - 'time': time, - }) - -report_sxw.report_sxw('report.mrp.production.order','mrp.production','addons/mrp/report/order.rml',parser=order,header='internal') - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from openerp.report import report_sxw + +class order(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(order, self).__init__(cr, uid, name, context=context) + self.localcontext.update({'time': time}) + + +report_sxw.report_sxw('report.mrp.production.order', 'mrp.production', 'addons/mrp/report/order.rml', parser=order, header='internal') \ No newline at end of file diff --git a/addons/mrp/report/price.py b/addons/mrp/report/price.py index e953ce942b0f1..a0fe61971e32f 100644 --- a/addons/mrp/report/price.py +++ b/addons/mrp/report/price.py @@ -1,226 +1,152 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import time -from openerp import pooler -from openerp.report.interface import report_rml -from openerp.tools import to_xml -from openerp.report import report_sxw -from datetime import datetime -from openerp.tools.translate import _ - -class report_custom(report_rml): - def create_xml(self, cr, uid, ids, datas, context=None): - number = (datas.get('form', False) and datas['form']['number']) or 1 - pool = pooler.get_pool(cr.dbname) - product_pool = pool.get('product.product') - product_uom_pool = pool.get('product.uom') - supplier_info_pool = pool.get('product.supplierinfo') - workcenter_pool = pool.get('mrp.workcenter') - user_pool = pool.get('res.users') - bom_pool = pool.get('mrp.bom') - pricelist_pool = pool.get('product.pricelist') - rml_obj=report_sxw.rml_parse(cr, uid, product_pool._name,context) - rml_obj.localcontext.update({'lang':context.get('lang',False)}) - company_currency = user_pool.browse(cr, uid, uid).company_id.currency_id - company_currency_symbol = company_currency.symbol or company_currency.name - product_uom_digits = rml_obj.get_digits(dp='Product Unit of Measure') - purchase_price_digits = rml_obj.get_digits(dp='Product Price') - def process_bom(bom, currency_id, factor=1): - xml = '' - sum = 0 - sum_strd = 0 - prod = product_pool.browse(cr, uid, bom['product_id']) - - prod_name = to_xml(bom['name']) - prod_qtty = factor * bom['product_qty'] - product_uom = product_uom_pool.browse(cr, uid, bom['product_uom'], context=context) - product_uom_name = to_xml(product_uom.name) - main_sp_price, main_sp_name , main_strd_price = '','','' - sellers, sellers_price = '','' - - if prod.seller_id: - main_sp_name = '- '+ to_xml(prod.seller_id.name) +'\r\n' - pricelist = prod.seller_id.property_product_pricelist_purchase - price = pricelist_pool.price_get(cr,uid,[pricelist.id], - prod.id, number*prod_qtty or 1.0, prod.seller_id.id, { - 'uom': prod.uom_po_id.id, - 'date': time.strftime('%Y-%m-%d'), - })[pricelist.id] - main_sp_price = """"""+rml_obj.formatLang(price, digits=purchase_price_digits)+' '+ (company_currency_symbol)+"""\r\n""" - sum += prod_qtty*price - std_price = product_uom_pool._compute_price(cr, uid, prod.uom_id.id, prod.standard_price, to_uom_id=product_uom.id) - main_strd_price = str(std_price) + '\r\n' - sum_strd = prod_qtty*std_price - for seller_id in prod.seller_ids: - if seller_id.name.id == prod.seller_id.id: - continue - sellers += '- '+ to_xml(seller_id.name.name) +'\r\n' - pricelist = seller_id.name.property_product_pricelist_purchase - price = pricelist_pool.price_get(cr,uid,[pricelist.id], - prod.id, number*prod_qtty or 1.0, seller_id.name.id, { - 'uom': prod.uom_po_id.id, - 'date': time.strftime('%Y-%m-%d'), - })[pricelist.id] - sellers_price += """"""+rml_obj.formatLang(price, digits=purchase_price_digits) +' '+ (company_currency_symbol) +"""\r\n""" - xml += """ """+ prod_name +""" - """+ main_sp_name + sellers + """ - """+ rml_obj.formatLang(prod_qtty, digits=product_uom_digits) +' '+ product_uom_name +""" - """+ rml_obj.formatLang(float(main_strd_price), digits=purchase_price_digits) +' '+ (company_currency_symbol) +""" - """ + main_sp_price + sellers_price + """'""" - - xml += '' - return xml, sum, sum_strd - - def process_workcenter(wrk): - workcenter = workcenter_pool.browse(cr, uid, wrk['workcenter_id']) - cost_cycle = wrk['cycle']*workcenter.costs_cycle - cost_hour = wrk['hour']*workcenter.costs_hour - total = cost_cycle + cost_hour - xml = '' - xml += "" + to_xml(workcenter.name) + '' - xml += "" - xml += """"""+rml_obj.formatLang(cost_cycle)+' '+ (company_currency_symbol) + """""" - xml += """"""+rml_obj.formatLang(cost_hour)+' '+ (company_currency_symbol) + """""" - xml += """"""+rml_obj.formatLang(cost_hour + cost_cycle)+' '+ (company_currency_symbol) + """""" - xml += '' - - return xml, total - - - xml = '' - config_start = """ - - """ + to_xml(rml_obj.formatLang(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),date_time=True)) + """ - %s - 210.00mm,297.00mm - 595.27 - 841.88 - 55.00mm,58.00mm,29.00mm,29.00mm,29.00mm - """ % to_xml(user_pool.browse(cr, uid, uid).company_id.name) - config_stop = """ - Generated by OpenERP - - """ - - workcenter_header = """ - - - %s - - %s - %s - %s - - - """ % (_('Work Center name'), _('Cycles Cost'), _('Hourly Cost'),_('Work Cost')) - prod_header = """ - - %s - %s - %s - %s - %s - - """ % (_('Components'), _('Components suppliers'), _('Quantity'),_('Cost Price per Unit of Measure'), _('Supplier Price per Unit of Measure')) - - for product in product_pool.browse(cr, uid, ids, context=context): - product_uom_name = to_xml(product.uom_id.name) - bom_id = bom_pool._bom_find(cr, uid, product.id, product.uom_id.id) - title = "%s" %(_("Cost Structure")) - title += "%s" % (to_xml(product.name)) - xml += "" + title + prod_header + "" - if not bom_id: - total_strd = number * product.standard_price - total = number * product_pool.price_get(cr, uid, [product.id], 'standard_price')[product.id] - xml += """ - - - - - - - - - - - """ - xml += """ - """ + _('Total Cost of %s %s') % (str(number), product_uom_name) + """: - - - """+ rml_obj.formatLang(total_strd, digits=purchase_price_digits) +' '+ (company_currency_symbol) + """ - """+ rml_obj.formatLang(total, digits=purchase_price_digits) +' '+ (company_currency_symbol) + """ - '""" - else: - bom = bom_pool.browse(cr, uid, bom_id, context=context) - factor = number * product.uom_id.factor / bom.product_uom.factor - sub_boms = bom_pool._bom_explode(cr, uid, bom, factor / bom.product_qty) - total = 0 - total_strd = 0 - parent_bom = { - 'product_qty': bom.product_qty, - 'name': bom.product_id.name, - 'product_uom': bom.product_uom.id, - 'product_id': bom.product_id.id - } - xml_tmp = '' - for sub_bom in (sub_boms and sub_boms[0]) or [parent_bom]: - txt, sum, sum_strd = process_bom(sub_bom, company_currency.id) - xml_tmp += txt - total += sum - total_strd += sum_strd - - xml += "" + xml_tmp + '' - xml += """ - """ + _('Components Cost of %s %s') % (str(number), product_uom_name) + """: - - - """+ rml_obj.formatLang(total_strd, digits=purchase_price_digits) +' '+ (company_currency_symbol) + """ - - '""" - - total2 = 0 - xml_tmp = '' - for wrk in (sub_boms and sub_boms[1]): - txt, sum = process_workcenter(wrk) - xml_tmp += txt - total2 += sum - if xml_tmp: - xml += workcenter_header - xml += "" + xml_tmp + '' - xml += """ - """ + _('Work Cost of %s %s') % (str(number), product_uom_name) +""": - - - - """+ rml_obj.formatLang(total2, digits=purchase_price_digits) +' '+ (company_currency_symbol) +""" - '""" - xml += """ - """ + _('Total Cost of %s %s') % (str(number), product_uom_name) + """: - - - """+ rml_obj.formatLang(total_strd+total2, digits=purchase_price_digits) +' '+ (company_currency_symbol) + """ - - '""" - - xml = '' + config_start + config_stop + xml + '' - - return xml - -report_custom('report.product.price', 'product.product', '', 'addons/mrp/report/price.xsl') - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import time +from openerp import pooler +from openerp.report.interface import report_rml +from openerp.tools import to_xml +from openerp.report import report_sxw +from datetime import datetime +from openerp.tools.translate import _ + +class report_custom(report_rml): + + def create_xml(self, cr, uid, ids, datas, context = None): + number = datas.get('form', False) and datas['form']['number'] or 1 + pool = pooler.get_pool(cr.dbname) + product_pool = pool.get('product.product') + product_uom_pool = pool.get('product.uom') + supplier_info_pool = pool.get('product.supplierinfo') + workcenter_pool = pool.get('mrp.workcenter') + user_pool = pool.get('res.users') + bom_pool = pool.get('mrp.bom') + pricelist_pool = pool.get('product.pricelist') + rml_obj = report_sxw.rml_parse(cr, uid, product_pool._name, context) + rml_obj.localcontext.update({'lang': context.get('lang', False)}) + company_currency = user_pool.browse(cr, uid, uid).company_id.currency_id + company_currency_symbol = company_currency.symbol or company_currency.name + + def process_bom(bom, currency_id, factor = 1): + xml = '' + sum = 0 + sum_strd = 0 + prod = product_pool.browse(cr, uid, bom['product_id']) + prod_name = to_xml(bom['name']) + prod_qtty = factor * bom['product_qty'] + product_uom = product_uom_pool.browse(cr, uid, bom['product_uom'], context=context) + product_uom_name = to_xml(product_uom.name) + main_sp_price, main_sp_name, main_strd_price = ('', '', '') + sellers, sellers_price = ('', '') + if prod.seller_id: + main_sp_name = '- ' + to_xml(prod.seller_id.name) + '\r\n' + pricelist = prod.seller_id.property_product_pricelist_purchase + price = pricelist_pool.price_get(cr, uid, [pricelist.id], prod.id, number * prod_qtty or 1.0, prod.seller_id.id, {'uom': prod.uom_po_id.id, + 'date': time.strftime('%Y-%m-%d')})[pricelist.id] + main_sp_price = '' + rml_obj.formatLang(price) + ' ' + company_currency_symbol + '\r\n' + sum += prod_qtty * price + std_price = product_uom_pool._compute_price(cr, uid, prod.uom_id.id, prod.standard_price, to_uom_id=product_uom.id) + main_strd_price = str(std_price) + '\r\n' + sum_strd = prod_qtty * std_price + for seller_id in prod.seller_ids: + sellers += '- ' + to_xml(seller_id.name.name) + '\r\n' + pricelist = seller_id.name.property_product_pricelist_purchase + price = pricelist_pool.price_get(cr, uid, [pricelist.id], prod.id, number * prod_qtty or 1.0, seller_id.name.id, {'uom': prod.uom_po_id.id, + 'date': time.strftime('%Y-%m-%d')})[pricelist.id] + sellers_price += '' + rml_obj.formatLang(price) + ' ' + company_currency_symbol + '\r\n' + + xml += " " + prod_name + " \n " + main_sp_name + sellers + " \n " + rml_obj.formatLang(prod_qtty) + ' ' + product_uom_name + "\n " + rml_obj.formatLang(float(main_strd_price)) + ' ' + company_currency_symbol + "\n " + main_sp_price + sellers_price + "'" + xml += '' + return (xml, sum, sum_strd) + + def process_workcenter(wrk): + workcenter = workcenter_pool.browse(cr, uid, wrk['workcenter_id']) + cost_cycle = wrk['cycle'] * workcenter.costs_cycle + cost_hour = wrk['hour'] * workcenter.costs_hour + total = cost_cycle + cost_hour + xml = '' + xml += "" + to_xml(workcenter.name) + '' + xml += '' + xml += "" + rml_obj.formatLang(cost_cycle) + ' ' + company_currency_symbol + '' + xml += "" + rml_obj.formatLang(cost_hour) + ' ' + company_currency_symbol + '' + xml += "" + rml_obj.formatLang(cost_hour + cost_cycle) + ' ' + company_currency_symbol + '' + xml += '' + return (xml, total) + + xml = '' + config_start = '\n \n ' + to_xml(rml_obj.formatLang(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), date_time=True)) + '\n %s\n 210.00mm,297.00mm\n 595.27\n 841.88\n 55.00mm,58.00mm,29.00mm,29.00mm,29.00mm\n ' % to_xml(user_pool.browse(cr, uid, uid).company_id.name) + config_stop = '\n Generated by OpenERP\n \n ' + workcenter_header = "\n \n \n %s\n \n %s\n %s\n %s\n \n \n " % (_('Work Center name'), + _('Cycles Cost'), + _('Hourly Cost'), + _('Work Cost')) + prod_header = "\n \n %s\n %s\n %s\n %s\n %s\n \n " % (_('Components'), + _('Components suppliers'), + _('Quantity'), + _('Cost Price per Unit of Measure'), + _('Supplier Price per Unit of Measure')) + purchase_price_digits = rml_obj.get_digits(dp='Product Price') + for product in product_pool.browse(cr, uid, ids, context=context): + product_uom_name = to_xml(product.uom_id.name) + bom_id = bom_pool._bom_find(cr, uid, product.id, product.uom_id.id) + title = '%s' % _('Cost Structure') + title += '%s' % to_xml(product.name) + xml += "" + title + prod_header + '' + if not bom_id: + total_strd = number * product.standard_price + total = number * product_pool.price_get(cr, uid, [product.id], 'standard_price')[product.id] + xml += "\n -\n -\n -\n -\n -\n " + xml += " \n " + _('Total Cost of %s %s') % (str(number), product_uom_name) + ": \n \n \n " + rml_obj.formatLang(total_strd, digits=purchase_price_digits) + ' ' + company_currency_symbol + "\n " + rml_obj.formatLang(total, digits=purchase_price_digits) + ' ' + company_currency_symbol + "\n '" + else: + bom = bom_pool.browse(cr, uid, bom_id, context=context) + factor = number * product.uom_id.factor / bom.product_uom.factor + sub_boms = bom_pool._bom_explode(cr, uid, bom, factor / bom.product_qty) + total = 0 + total_strd = 0 + parent_bom = {'product_qty': bom.product_qty, + 'name': bom.product_id.name, + 'product_uom': bom.product_uom.id, + 'product_id': bom.product_id.id} + xml_tmp = '' + for sub_bom in sub_boms and sub_boms[0] or [parent_bom]: + txt, sum, sum_strd = process_bom(sub_bom, company_currency.id) + xml_tmp += txt + total += sum + total_strd += sum_strd + + xml += "" + xml_tmp + '' + xml += " \n " + _('Components Cost of %s %s') % (str(number), product_uom_name) + ": \n \n \n " + rml_obj.formatLang(total_strd, digits=purchase_price_digits) + ' ' + company_currency_symbol + "\n \n '" + total2 = 0 + xml_tmp = '' + for wrk in sub_boms and sub_boms[1]: + txt, sum = process_workcenter(wrk) + xml_tmp += txt + total2 += sum + + if xml_tmp: + xml += workcenter_header + xml += "" + xml_tmp + '' + xml += " \n " + _('Work Cost of %s %s') % (str(number), product_uom_name) + ": \n \n \n \n " + rml_obj.formatLang(total2, digits=purchase_price_digits) + ' ' + company_currency_symbol + "\n '" + xml += " \n " + _('Total Cost of %s %s') % (str(number), product_uom_name) + ": \n \n \n " + rml_obj.formatLang(total_strd + total2, digits=purchase_price_digits) + ' ' + company_currency_symbol + "\n \n '" + + xml = '' + config_start + config_stop + xml + '' + return xml + + +report_custom('report.product.price', 'product.product', '', 'addons/mrp/report/price.xsl') \ No newline at end of file diff --git a/addons/mrp/report/workcenter_load.py b/addons/mrp/report/workcenter_load.py index ffce2da9d3157..520c8584388ac 100644 --- a/addons/mrp/report/workcenter_load.py +++ b/addons/mrp/report/workcenter_load.py @@ -1,207 +1,175 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.report.render import render -from openerp.report.interface import report_int -import time -from datetime import date, datetime, timedelta -from dateutil.relativedelta import relativedelta - -from openerp.report.misc import choice_colors - -import StringIO - -from pychart import * - -theme.use_color = 1 - -# -# TODO: Bad code, seems buggy, TO CHECK ! -# - -class external_pdf(render): - def __init__(self, pdf): - render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - - def _render(self): - return self.pdf - -class report_custom(report_int): - def _compute_dates(self, time_unit, start, stop): - if not stop: - stop = start - if time_unit == 'month': - dates = {} - a = int(start.split("-")[0])*12 + int(start.split("-")[1]) - z = int(stop.split("-")[0])*12 + int(stop.split("-")[1]) + 1 - for i in range(a,z): - year = i/12 - month = i%12 - if month == 0: - year -= 1 - month = 12 - months = {1:"January",2:"February",3:"March",4:"April",5:"May",6:"June",7:"July",8:"August",9:"September",10:"October",11:"November",12:"December"} - dates[i] = { - 'name' :months[month], - 'start':(datetime(year, month, 2) + relativedelta(day=1)).strftime('%Y-%m-%d'), - 'stop' :(datetime(year, month, 2) + relativedelta(day=31)).strftime('%Y-%m-%d'), - } - return dates - elif time_unit == 'week': - dates = {} - start_week = date(int(start.split("-")[0]),int(start.split("-")[1]),int(start.split("-")[2])).isocalendar() - end_week = date(int(stop.split("-")[0]),int(stop.split("-")[1]),int(stop.split("-")[2])).isocalendar() - a = int(start.split("-")[0])*52 + start_week[1] - z = int(stop.split("-")[0])*52 + end_week[1] - for i in range(a,z+1): - year = i/52 - week = i%52 - d = date(year, 1, 1) - - dates[i] = { - 'name' :"Week #%d" % week, - 'start':(d + timedelta(days=-d.weekday(), weeks=week)).strftime('%Y-%m-%d'), - 'stop' :(d + timedelta(days=6-d.weekday(), weeks=week)).strftime('%Y-%m-%d'), - } - return dates - else: # time_unit = day - dates = {} - a = datetime(int(start.split("-")[0]),int(start.split("-")[1]),int(start.split("-")[2])) - z = datetime(int(stop.split("-")[0]),int(stop.split("-")[1]),int(stop.split("-")[2])) - i = a - while i <= z: - dates[map(int,i.strftime('%Y%m%d').split())[0]] = { - 'name' :i.strftime('%Y-%m-%d'), - 'start':i.strftime('%Y-%m-%d'), - 'stop' :i.strftime('%Y-%m-%d'), - } - i = i + relativedelta(days=+1) - return dates - return {} - - def create(self, cr, uid, ids, datas, context=None): - assert len(ids), 'You should provide some ids!' - colors = choice_colors(len(ids)) - cr.execute( - "SELECT MAX(mrp_production.date_planned) AS stop,MIN(mrp_production.date_planned) AS start "\ - "FROM mrp_workcenter, mrp_production, mrp_production_workcenter_line "\ - "WHERE mrp_production_workcenter_line.production_id=mrp_production.id "\ - "AND mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id "\ - "AND mrp_production.state NOT IN ('cancel','done') "\ - "AND mrp_workcenter.id IN %s",(tuple(ids),)) - res = cr.dictfetchone() - if not res['stop']: - res['stop'] = time.strftime('%Y-%m-%d %H:%M:%S') - if not res['start']: - res['start'] = time.strftime('%Y-%m-%d %H:%M:%S') - dates = self._compute_dates(datas['form']['time_unit'], res['start'][:10], res['stop'][:10]) - dates_list = dates.keys() - dates_list.sort() - x_index = [] - for date in dates_list: - x_index.append((dates[date]['name'], date)) - pdf_string = StringIO.StringIO() - can = canvas.init(fname=pdf_string, format='pdf') - can.set_title("Work Center Loads") - chart_object.set_defaults(line_plot.T, line_style=None) - if datas['form']['measure_unit'] == 'cycles': - y_label = "Load (Cycles)" - else: - y_label = "Load (Hours)" - - # For add the report header on the top of the report. - tb = text_box.T(loc=(300, 500), text="/hL/15/bWork Center Loads", line_style=None) - tb.draw() - ar = area.T(legend = legend.T(), - x_grid_style = line_style.gray70_dash1, - x_axis = axis.X(label="Periods", format="/a90/hC%s"), - x_coord = category_coord.T(x_index, 0), - y_axis = axis.Y(label=y_label), - y_range = (0, None), - size = (640,480)) - bar_plot.fill_styles.reset(); - # select workcenters - cr.execute( - "SELECT mw.id, rs.name FROM mrp_workcenter mw, resource_resource rs " \ - "WHERE mw.id IN %s and mw.resource_id=rs.id " \ - "ORDER BY mw.id" ,(tuple(ids),)) - workcenters = cr.dictfetchall() - - data = [] - for date in dates_list: - vals = [] - for workcenter in workcenters: - cr.execute("SELECT SUM(mrp_production_workcenter_line.hour) AS hours, SUM(mrp_production_workcenter_line.cycle) AS cycles, \ - resource_resource.name AS name, mrp_workcenter.id AS id \ - FROM mrp_production_workcenter_line, mrp_production, mrp_workcenter, resource_resource \ - WHERE (mrp_production_workcenter_line.production_id=mrp_production.id) \ - AND (mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id) \ - AND (mrp_workcenter.resource_id=resource_resource.id) \ - AND (mrp_workcenter.id=%s) \ - AND (mrp_production.date_planned BETWEEN %s AND %s) \ - GROUP BY mrp_production_workcenter_line.workcenter_id, resource_resource.name, mrp_workcenter.id \ - ORDER BY mrp_workcenter.id", (workcenter['id'], dates[date]['start'] + ' 00:00:00', dates[date]['stop'] + ' 23:59:59')) - res = cr.dictfetchall() - if not res: - vals.append(0.0) - else: - if datas['form']['measure_unit'] == 'cycles': - vals.append(res[0]['cycles'] or 0.0) - else: - vals.append(res[0]['hours'] or 0.0) - - toto = [dates[date]['name']] - for val in vals: - toto.append(val) - data.append(toto) - - workcenter_num = 0 - for workcenter in workcenters: - f = fill_style.Plain() - f.bgcolor = colors[workcenter_num] - ar.add_plot(bar_plot.T(label=workcenter['name'], data=data, fill_style=f, hcol=workcenter_num+1, cluster=(workcenter_num, len(res)))) - workcenter_num += 1 - - if (not data) or (len(data[0]) <= 1): - ar = self._empty_graph(time.strftime('%Y-%m-%d')) - ar.draw(can) - # close canvas so that the file is written to "disk" - can.close() - self.obj = external_pdf(pdf_string.getvalue()) - self.obj.render() - pdf_string.close() - return (self.obj.pdf, 'pdf') - - def _empty_graph(self, date): - data = [[date, 0]] - ar = area.T(x_coord = category_coord.T(data, 0), y_range = (0, None), - x_axis = axis.X(label="Periods"), - y_axis = axis.Y(label="Load")) - ar.add_plot(bar_plot.T(data = data, label="No production order")) - return ar - -report_custom('report.mrp.workcenter.load') - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.report.render import render +from openerp.report.interface import report_int +import time +from datetime import date, datetime, timedelta +from dateutil.relativedelta import relativedelta +from openerp.report.misc import choice_colors +import StringIO +from pychart import * +theme.use_color = 1 + +class external_pdf(render): + + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type = 'pdf' + + def _render(self): + return self.pdf + + +class report_custom(report_int): + + def _compute_dates(self, time_unit, start, stop): + if not stop: + stop = start + if time_unit == 'month': + dates = {} + a = int(start.split('-')[0]) * 12 + int(start.split('-')[1]) + z = int(stop.split('-')[0]) * 12 + int(stop.split('-')[1]) + 1 + for i in range(a, z): + year = i / 12 + month = i % 12 + if month == 0: + year -= 1 + month = 12 + months = {1: 'January', + 2: 'February', + 3: 'March', + 4: 'April', + 5: 'May', + 6: 'June', + 7: 'July', + 8: 'August', + 9: 'September', + 10: 'October', + 11: 'November', + 12: 'December'} + dates[i] = {'name': months[month], + 'start': (datetime(year, month, 2) + relativedelta(day=1)).strftime('%Y-%m-%d'), + 'stop': (datetime(year, month, 2) + relativedelta(day=31)).strftime('%Y-%m-%d')} + + return dates + elif time_unit == 'week': + dates = {} + start_week = date(int(start.split('-')[0]), int(start.split('-')[1]), int(start.split('-')[2])).isocalendar() + end_week = date(int(stop.split('-')[0]), int(stop.split('-')[1]), int(stop.split('-')[2])).isocalendar() + a = int(start.split('-')[0]) * 52 + start_week[1] + z = int(stop.split('-')[0]) * 52 + end_week[1] + for i in range(a, z + 1): + year = i / 52 + week = i % 52 + d = date(year, 1, 1) + dates[i] = {'name': 'Week #%d' % week, + 'start': (d + timedelta(days=-d.weekday(), weeks=week)).strftime('%Y-%m-%d'), + 'stop': (d + timedelta(days=6 - d.weekday(), weeks=week)).strftime('%Y-%m-%d')} + + return dates + else: + dates = {} + a = datetime(int(start.split('-')[0]), int(start.split('-')[1]), int(start.split('-')[2])) + z = datetime(int(stop.split('-')[0]), int(stop.split('-')[1]), int(stop.split('-')[2])) + i = a + while i <= z: + dates[map(int, i.strftime('%Y%m%d').split())[0]] = {'name': i.strftime('%Y-%m-%d'), + 'start': i.strftime('%Y-%m-%d'), + 'stop': i.strftime('%Y-%m-%d')} + i = i + relativedelta(days=+1) + + return dates + return {} + + def create(self, cr, uid, ids, datas, context = None): + if not len(ids): + raise AssertionError('You should provide some ids!') + colors = choice_colors(len(ids)) + cr.execute("SELECT MAX(mrp_production.date_planned) AS stop,MIN(mrp_production.date_planned) AS start FROM mrp_workcenter, mrp_production, mrp_production_workcenter_line WHERE mrp_production_workcenter_line.production_id=mrp_production.id AND mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id AND mrp_production.state NOT IN ('cancel','done') AND mrp_workcenter.id IN %s", (tuple(ids),)) + res = cr.dictfetchone() + if not res['stop']: + res['stop'] = time.strftime('%Y-%m-%d %H:%M:%S') + if not res['start']: + res['start'] = time.strftime('%Y-%m-%d %H:%M:%S') + dates = self._compute_dates(datas['form']['time_unit'], res['start'][:10], res['stop'][:10]) + dates_list = dates.keys() + dates_list.sort() + x_index = [] + for date in dates_list: + x_index.append((dates[date]['name'], date)) + + pdf_string = StringIO.StringIO() + can = canvas.init(fname=pdf_string, format='pdf') + can.set_title('Work Center Loads') + chart_object.set_defaults(line_plot.T, line_style=None) + if datas['form']['measure_unit'] == 'cycles': + y_label = 'Load (Cycles)' + else: + y_label = 'Load (Hours)' + tb = text_box.T(loc=(300, 500), text='/hL/15/bWork Center Loads', line_style=None) + tb.draw() + ar = area.T(legend=legend.T(), x_grid_style=line_style.gray70_dash1, x_axis=axis.X(label='Periods', format='/a90/hC%s'), x_coord=category_coord.T(x_index, 0), y_axis=axis.Y(label=y_label), y_range=(0, None), size=(640, 480)) + bar_plot.fill_styles.reset() + cr.execute('SELECT mw.id, rs.name FROM mrp_workcenter mw, resource_resource rs WHERE mw.id IN %s and mw.resource_id=rs.id ORDER BY mw.id', (tuple(ids),)) + workcenters = cr.dictfetchall() + data = [] + for date in dates_list: + vals = [] + for workcenter in workcenters: + cr.execute('SELECT SUM(mrp_production_workcenter_line.hour) AS hours, SUM(mrp_production_workcenter_line.cycle) AS cycles, resource_resource.name AS name, mrp_workcenter.id AS id FROM mrp_production_workcenter_line, mrp_production, mrp_workcenter, resource_resource WHERE (mrp_production_workcenter_line.production_id=mrp_production.id) AND (mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id) AND (mrp_workcenter.resource_id=resource_resource.id) AND (mrp_workcenter.id=%s) AND (mrp_production.date_planned BETWEEN %s AND %s) GROUP BY mrp_production_workcenter_line.workcenter_id, resource_resource.name, mrp_workcenter.id ORDER BY mrp_workcenter.id', (workcenter['id'], dates[date]['start'] + ' 00:00:00', dates[date]['stop'] + ' 23:59:59')) + res = cr.dictfetchall() + if not res: + vals.append(0.0) + elif datas['form']['measure_unit'] == 'cycles': + vals.append(res[0]['cycles'] or 0.0) + else: + vals.append(res[0]['hours'] or 0.0) + + toto = [dates[date]['name']] + for val in vals: + toto.append(val) + + data.append(toto) + + workcenter_num = 0 + for workcenter in workcenters: + f = fill_style.Plain() + f.bgcolor = colors[workcenter_num] + ar.add_plot(bar_plot.T(label=workcenter['name'], data=data, fill_style=f, hcol=workcenter_num + 1, cluster=(workcenter_num, len(res)))) + workcenter_num += 1 + + ar = (not data or len(data[0]) <= 1) and self._empty_graph(time.strftime('%Y-%m-%d')) + ar.draw(can) + can.close() + self.obj = external_pdf(pdf_string.getvalue()) + self.obj.render() + pdf_string.close() + return (self.obj.pdf, 'pdf') + + def _empty_graph(self, date): + data = [[date, 0]] + ar = area.T(x_coord=category_coord.T(data, 0), y_range=(0, None), x_axis=axis.X(label='Periods'), y_axis=axis.Y(label='Load')) + ar.add_plot(bar_plot.T(data=data, label='No production order')) + return ar + + +report_custom('report.mrp.workcenter.load') \ No newline at end of file diff --git a/addons/mrp/res_config.py b/addons/mrp/res_config.py index cb5c35a3b0d25..763747d42b6b5 100644 --- a/addons/mrp/res_config.py +++ b/addons/mrp/res_config.py @@ -1,76 +1,35 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (C) 2004-2012 OpenERP S.A. (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -from openerp import pooler -from openerp.tools.translate import _ - -class mrp_config_settings(osv.osv_memory): - _name = 'mrp.config.settings' - _inherit = 'res.config.settings' - - _columns = { - 'module_mrp_repair': fields.boolean("Manage repairs of products ", - help="""Allows to manage all product repairs. - * Add/remove products in the reparation - * Impact for stocks - * Invoicing (products and/or services) - * Warranty concept - * Repair quotation report - * Notes for the technician and for the final customer. - This installs the module mrp_repair."""), - 'module_mrp_operations': fields.boolean("Allow detailed planning of work orders", - help="""This allows to add state, date_start,date_stop in production order operation lines (in the "Work Centers" tab). - This installs the module mrp_operations."""), - 'module_mrp_byproduct': fields.boolean("Produce several products from one manufacturing order", - help="""You can configure by-products in the bill of material. - Without this module: A + B + C -> D. - With this module: A + B + C -> D + E. - This installs the module mrp_byproduct."""), - 'module_mrp_jit': fields.boolean("Generate procurement in real time", - help="""This allows Just In Time computation of procurement orders. - All procurement orders will be processed immediately, which could in some - cases entail a small performance impact. - This installs the module mrp_jit."""), - 'module_stock_no_autopicking': fields.boolean("Manage manual picking to fulfill manufacturing orders ", - help="""This module allows an intermediate picking process to provide raw materials to production orders. - For example to manage production made by your suppliers (sub-contracting). - To achieve this, set the assembled product which is sub-contracted to "No Auto-Picking" - and put the location of the supplier in the routing of the assembly operation. - This installs the module stock_no_autopicking."""), - 'group_mrp_routings': fields.boolean("Manage routings and work orders ", - implied_group='mrp.group_mrp_routings', - help="""Routings allow you to create and manage the manufacturing operations that should be followed - within your work centers in order to produce a product. They are attached to bills of materials - that will define the required raw materials."""), - 'group_mrp_properties': fields.boolean("Allow several bill of materials per product using properties", - implied_group='product.group_mrp_properties', - help="""The selection of the right Bill of Material to use will depend on the properties specified on the sales order and the Bill of Material."""), - 'module_product_manufacturer': fields.boolean("Define manufacturers on products ", - help="""This allows you to define the following for a product: - * Manufacturer - * Manufacturer Product Name - * Manufacturer Product Code - * Product Attributes. - This installs the module product_manufacturer."""), - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +from openerp import pooler +from openerp.tools.translate import _ + +class mrp_config_settings(osv.osv_memory): + _name = 'mrp.config.settings' + _inherit = 'res.config.settings' + _columns = {'module_mrp_repair': fields.boolean('Manage repairs of products ', help='Allows to manage all product repairs.\n * Add/remove products in the reparation\n * Impact for stocks\n * Invoicing (products and/or services)\n * Warranty concept\n * Repair quotation report\n * Notes for the technician and for the final customer.\n This installs the module mrp_repair.'), + 'module_mrp_operations': fields.boolean('Allow detailed planning of work orders', help='This allows to add state, date_start,date_stop in production order operation lines (in the "Work Centers" tab).\n This installs the module mrp_operations.'), + 'module_mrp_byproduct': fields.boolean('Produce several products from one manufacturing order', help='You can configure by-products in the bill of material.\n Without this module: A + B + C -> D.\n With this module: A + B + C -> D + E.\n This installs the module mrp_byproduct.'), + 'module_mrp_jit': fields.boolean('Generate procurement in real time', help='This allows Just In Time computation of procurement orders.\n All procurement orders will be processed immediately, which could in some\n cases entail a small performance impact.\n This installs the module mrp_jit.'), + 'module_stock_no_autopicking': fields.boolean('Manage manual picking to fulfill manufacturing orders ', help='This module allows an intermediate picking process to provide raw materials to production orders.\n For example to manage production made by your suppliers (sub-contracting).\n To achieve this, set the assembled product which is sub-contracted to "No Auto-Picking"\n and put the location of the supplier in the routing of the assembly operation.\n This installs the module stock_no_autopicking.'), + 'group_mrp_routings': fields.boolean('Manage routings and work orders ', implied_group='mrp.group_mrp_routings', help='Routings allow you to create and manage the manufacturing operations that should be followed\n within your work centers in order to produce a product. They are attached to bills of materials\n that will define the required raw materials.'), + 'group_mrp_properties': fields.boolean('Allow several bill of materials per product using properties', implied_group='product.group_mrp_properties', help='The selection of the right Bill of Material to use will depend on the properties specified on the sales order and the Bill of Material.'), + 'module_product_manufacturer': fields.boolean('Define manufacturers on products ', help='This allows you to define the following for a product:\n * Manufacturer\n * Manufacturer Product Name\n * Manufacturer Product Code\n * Product Attributes.\n This installs the module product_manufacturer.')} \ No newline at end of file diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv index 2d0a478c33db4..7a94b1a660c96 100644 --- a/addons/mrp/security/ir.model.access.csv +++ b/addons/mrp/security/ir.model.access.csv @@ -1,5 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_account_analytic_line_user,account.analytic.line,account.model_account_analytic_line,group_mrp_user,1,1,1,0 access_mrp_workcenter,mrp.workcenter,model_mrp_workcenter,mrp.group_mrp_user,1,0,0,0 access_mrp_routing,mrp.routing,model_mrp_routing,mrp.group_mrp_user,1,0,0,0 access_mrp_routing_workcenter,mrp.routing.workcenter,model_mrp_routing_workcenter,mrp.group_mrp_user,1,0,0,0 diff --git a/addons/mrp/security/mrp_security.xml b/addons/mrp/security/mrp_security.xml index 9e5dc9197200b..35aae42b53d89 100644 --- a/addons/mrp/security/mrp_security.xml +++ b/addons/mrp/security/mrp_security.xml @@ -4,7 +4,6 @@ User - diff --git a/addons/mrp/stock.py b/addons/mrp/stock.py index d049aea380377..5ba68e3fabf64 100644 --- a/addons/mrp/stock.py +++ b/addons/mrp/stock.py @@ -1,189 +1,173 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields -from openerp.osv import osv -from openerp import netsvc - - -class StockMove(osv.osv): - _inherit = 'stock.move' - - _columns = { - 'production_id': fields.many2one('mrp.production', 'Production', select=True), - } - - def create_chained_picking(self, cr, uid, moves, context=None): - new_moves = super(StockMove, self).create_chained_picking(cr, uid, moves, context=context) - self.write(cr, uid, [x.id for x in new_moves], {'production_id': False}, context=context) - return new_moves - - def _action_explode(self, cr, uid, move, context=None): - """ Explodes pickings. - @param move: Stock moves - @return: True - """ - bom_obj = self.pool.get('mrp.bom') - move_obj = self.pool.get('stock.move') - procurement_obj = self.pool.get('procurement.order') - product_obj = self.pool.get('product.product') - wf_service = netsvc.LocalService("workflow") - processed_ids = [move.id] - if move.product_id.supply_method == 'produce': - bis = bom_obj.search(cr, uid, [ - ('product_id','=',move.product_id.id), - ('bom_id','=',False), - ('type','=','phantom')]) - if bis: - factor = move.product_qty - bom_point = bom_obj.browse(cr, uid, bis[0], context=context) - res = bom_obj._bom_explode(cr, uid, bom_point, factor, []) - for line in res[0]: - valdef = { - 'picking_id': move.picking_id.id, - 'product_id': line['product_id'], - 'product_uom': line['product_uom'], - 'product_qty': line['product_qty'], - 'product_uos': line['product_uos'], - 'product_uos_qty': line['product_uos_qty'], - 'move_dest_id': move.id, - 'state': 'draft', #will be confirmed below - 'name': line['name'], - 'move_history_ids': [(6,0,[move.id])], - 'move_history_ids2': [(6,0,[])], - 'procurements': [], - } - mid = move_obj.copy(cr, uid, move.id, default=valdef) - processed_ids.append(mid) - prodobj = product_obj.browse(cr, uid, line['product_id'], context=context) - proc_id = procurement_obj.create(cr, uid, { - 'name': (move.picking_id.origin or ''), - 'origin': (move.picking_id.origin or ''), - 'date_planned': move.date, - 'product_id': line['product_id'], - 'product_qty': line['product_qty'], - 'product_uom': line['product_uom'], - 'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False, - 'product_uos': line['product_uos'], - 'location_id': move.location_id.id, - 'procure_method': prodobj.procure_method, - 'move_id': mid, - }) - wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) - move_obj.write(cr, uid, [move.id], { - 'location_dest_id': move.location_id.id, # dummy move for the kit - 'auto_validate': True, - 'picking_id': False, - 'state': 'confirmed' - }) - for m in procurement_obj.search(cr, uid, [('move_id','=',move.id)], context): - wf_service.trg_validate(uid, 'procurement.order', m, 'button_confirm', cr) - wf_service.trg_validate(uid, 'procurement.order', m, 'button_wait_done', cr) - if processed_ids and move.state == 'assigned': - # Set the state of resulting moves according to 'assigned' as the original move is assigned - move_obj.write(cr, uid, list(set(processed_ids) - set([move.id])), {'state': 'assigned'}, context=context) - return processed_ids - - def action_consume(self, cr, uid, ids, product_qty, location_id=False, context=None): - """ Consumed product with specific quatity from specific source location. - @param product_qty: Consumed product quantity - @param location_id: Source location - @return: Consumed lines - """ - res = [] - production_obj = self.pool.get('mrp.production') - wf_service = netsvc.LocalService("workflow") - for move in self.browse(cr, uid, ids): - move.action_confirm(context) - new_moves = super(StockMove, self).action_consume(cr, uid, [move.id], product_qty, location_id, context=context) - production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])]) - for prod in production_obj.browse(cr, uid, production_ids, context=context): - if prod.state == 'confirmed': - production_obj.force_production(cr, uid, [prod.id]) - wf_service.trg_validate(uid, 'mrp.production', prod.id, 'button_produce', cr) - for new_move in new_moves: - if new_move == move.id: - #This move is already there in move lines of production order - continue - production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]}) - res.append(new_move) - return res - - def action_scrap(self, cr, uid, ids, product_qty, location_id, context=None): - """ Move the scrap/damaged product into scrap location - @param product_qty: Scraped product quantity - @param location_id: Scrap location - @return: Scraped lines - """ - res = [] - production_obj = self.pool.get('mrp.production') - wf_service = netsvc.LocalService("workflow") - for move in self.browse(cr, uid, ids, context=context): - new_moves = super(StockMove, self).action_scrap(cr, uid, [move.id], product_qty, location_id, context=context) - #If we are not scrapping our whole move, tracking and lot references must not be removed - #self.write(cr, uid, [move.id], {'prodlot_id': False, 'tracking_id': False}) - production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])]) - for prod_id in production_ids: - wf_service.trg_validate(uid, 'mrp.production', prod_id, 'button_produce', cr) - for new_move in new_moves: - production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]}) - res.append(new_move) - return res - -StockMove() - - -class StockPicking(osv.osv): - _inherit = 'stock.picking' - - # - # Explode picking by replacing phantom BoMs - # - def action_explode(self, cr, uid, move_ids, *args): - """Explodes moves by expanding kit components""" - move_obj = self.pool.get('stock.move') - todo = list(super(StockPicking, self).action_explode(cr, uid, move_ids, *args)) - for move in move_obj.browse(cr, uid, move_ids): - result = move_obj._action_explode(cr, uid, move) - moves = move_obj.browse(cr, uid, result) - todo.extend(move.id for move in moves if move.state not in ['confirmed', 'assigned', 'done']) - return list(set(todo)) - -StockPicking() - - -class split_in_production_lot(osv.osv_memory): - _inherit = "stock.move.split" - - def split(self, cr, uid, ids, move_ids, context=None): - """ Splits move lines into given quantities. - @param move_ids: Stock moves. - @return: List of new moves. - """ - new_moves = super(split_in_production_lot, self).split(cr, uid, ids, move_ids, context=context) - production_obj = self.pool.get('mrp.production') - production_ids = production_obj.search(cr, uid, [('move_lines', 'in', move_ids)]) - production_obj.write(cr, uid, production_ids, {'move_lines': [(4, m) for m in new_moves]}) - return new_moves - -split_in_production_lot() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields +from openerp.osv import osv +from openerp import netsvc + +class StockMove(osv.osv): + _inherit = 'stock.move' + _columns = {'production_id': fields.many2one('mrp.production', 'Production', select=True)} + + def create_chained_picking(self, cr, uid, moves, context = None): + new_moves = super(StockMove, self).create_chained_picking(cr, uid, moves, context=context) + self.write(cr, uid, [ x.id for x in new_moves ], {'production_id': False}, context=context) + return new_moves + + def _action_explode(self, cr, uid, move, context = None): + """ Explodes pickings. + @param move: Stock moves + @return: True + """ + bom_obj = self.pool.get('mrp.bom') + move_obj = self.pool.get('stock.move') + procurement_obj = self.pool.get('procurement.order') + product_obj = self.pool.get('product.product') + wf_service = netsvc.LocalService('workflow') + processed_ids = [move.id] + if move.product_id.supply_method == 'produce': + bis = bom_obj.search(cr, uid, [('product_id', '=', move.product_id.id), ('bom_id', '=', False), ('type', '=', 'phantom')]) + if bis: + factor = move.product_qty + bom_point = bom_obj.browse(cr, uid, bis[0], context=context) + res = bom_obj._bom_explode(cr, uid, bom_point, factor, []) + state = 'confirmed' + if move.state == 'assigned': + state = 'assigned' + for line in res[0]: + valdef = {'picking_id': move.picking_id.id, + 'product_id': line['product_id'], + 'product_uom': line['product_uom'], + 'product_qty': line['product_qty'], + 'product_uos': line['product_uos'], + 'product_uos_qty': line['product_uos_qty'], + 'move_dest_id': move.id, + 'state': state, + 'name': line['name'], + 'move_history_ids': [(6, 0, [move.id])], + 'move_history_ids2': [(6, 0, [])], + 'procurements': []} + mid = move_obj.copy(cr, uid, move.id, default=valdef) + processed_ids.append(mid) + prodobj = product_obj.browse(cr, uid, line['product_id'], context=context) + proc_id = procurement_obj.create(cr, uid, {'name': move.picking_id.origin or '', + 'origin': move.picking_id.origin or '', + 'date_planned': move.date, + 'product_id': line['product_id'], + 'product_qty': line['product_qty'], + 'product_uom': line['product_uom'], + 'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False, + 'product_uos': line['product_uos'], + 'location_id': move.location_id.id, + 'procure_method': prodobj.procure_method, + 'move_id': mid}) + wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) + + move_obj.write(cr, uid, [move.id], {'location_dest_id': move.location_id.id, + 'auto_validate': True, + 'picking_id': False, + 'state': 'confirmed'}) + for m in procurement_obj.search(cr, uid, [('move_id', '=', move.id)], context): + wf_service.trg_validate(uid, 'procurement.order', m, 'button_confirm', cr) + wf_service.trg_validate(uid, 'procurement.order', m, 'button_wait_done', cr) + + return processed_ids + + def action_consume(self, cr, uid, ids, product_qty, location_id = False, context = None): + """ Consumed product with specific quatity from specific source location. + @param product_qty: Consumed product quantity + @param location_id: Source location + @return: Consumed lines + """ + res = [] + production_obj = self.pool.get('mrp.production') + wf_service = netsvc.LocalService('workflow') + for move in self.browse(cr, uid, ids): + move.action_confirm(context) + new_moves = super(StockMove, self).action_consume(cr, uid, [move.id], product_qty, location_id, context=context) + production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])]) + for prod in production_obj.browse(cr, uid, production_ids, context=context): + if prod.state == 'confirmed': + production_obj.force_production(cr, uid, [prod.id]) + wf_service.trg_validate(uid, 'mrp.production', prod.id, 'button_produce', cr) + + for new_move in new_moves: + if new_move == move.id: + continue + production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]}) + res.append(new_move) + + return res + + def action_scrap(self, cr, uid, ids, product_qty, location_id, context = None): + """ Move the scrap/damaged product into scrap location + @param product_qty: Scraped product quantity + @param location_id: Scrap location + @return: Scraped lines + """ + res = [] + production_obj = self.pool.get('mrp.production') + wf_service = netsvc.LocalService('workflow') + for move in self.browse(cr, uid, ids, context=context): + new_moves = super(StockMove, self).action_scrap(cr, uid, [move.id], product_qty, location_id, context=context) + production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])]) + for prod_id in production_ids: + wf_service.trg_validate(uid, 'mrp.production', prod_id, 'button_produce', cr) + + for new_move in new_moves: + production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]}) + res.append(new_move) + + return res + + +StockMove() + +class StockPicking(osv.osv): + _inherit = 'stock.picking' + + def action_explode(self, cr, uid, move_ids, *args): + """Explodes moves by expanding kit components""" + move_obj = self.pool.get('stock.move') + todo = move_ids[:] + for move in move_obj.browse(cr, uid, move_ids): + todo.extend(move_obj._action_explode(cr, uid, move)) + + return list(set(todo)) + + +StockPicking() + +class split_in_production_lot(osv.osv_memory): + _inherit = 'stock.move.split' + + def split(self, cr, uid, ids, move_ids, context = None): + """ Splits move lines into given quantities. + @param move_ids: Stock moves. + @return: List of new moves. + """ + new_moves = super(split_in_production_lot, self).split(cr, uid, ids, move_ids, context=context) + production_obj = self.pool.get('mrp.production') + production_ids = production_obj.search(cr, uid, [('move_lines', 'in', move_ids)]) + production_obj.write(cr, uid, production_ids, {'move_lines': [ (4, m) for m in new_moves ]}) + return new_moves + + +split_in_production_lot() \ No newline at end of file diff --git a/addons/mrp/test/cancel_order.yml b/addons/mrp/test/cancel_order.yml index 2f721b6c653eb..9e197156b15ab 100644 --- a/addons/mrp/test/cancel_order.yml +++ b/addons/mrp/test/cancel_order.yml @@ -1,8 +1,3 @@ -- - MRP user can cancelled Production Order, so let's check data with giving the access rights of user. -- - !context - uid: 'res_users_mrp_user' - I first confirm order for PC Assemble SC349. - diff --git a/addons/mrp/test/order_demo.yml b/addons/mrp/test/order_demo.yml index 0e4914390fb66..ee21fee96df06 100644 --- a/addons/mrp/test/order_demo.yml +++ b/addons/mrp/test/order_demo.yml @@ -1,8 +1,3 @@ -- - MRP user can create Production Order, so let's check data with giving the access rights of user. -- - !context - uid: 'res_users_mrp_user' - I create Production Order of PC Assemble SC349 to produce 5.0 Unit. - diff --git a/addons/mrp/test/order_process.yml b/addons/mrp/test/order_process.yml index bbafe5b6b278d..e672517c5a0a0 100644 --- a/addons/mrp/test/order_process.yml +++ b/addons/mrp/test/order_process.yml @@ -1,8 +1,3 @@ -- - MRP user can doing all process related to Production Order, so let's check data with giving the access rights of user. -- - !context - uid: 'res_users_mrp_user' - I compute the production order. - @@ -19,7 +14,7 @@ Now I check workcenter lines. - !python {model: mrp.production}: | - from openerp.tools import float_compare + from tools import float_compare def assert_equals(value1, value2, msg, float_compare=float_compare): assert float_compare(value1, value2, precision_digits=2) == 0, msg order = self.browse(cr, uid, ref("mrp_production_test1"), context=context) @@ -28,7 +23,6 @@ I confirm the Production Order. - !workflow {model: mrp.production, action: button_confirm, ref: mrp_production_test1} - - I check details of Produce Move of Production Order to trace Final Product. - @@ -64,16 +58,6 @@ assert move_line.product_uos.id == order_line.product_uos.id, "UOS is not correspond in 'To consume line'." assert move_line.location_id.id == routing_loc or order.location_src_id.id, "Source location is not correspond in 'To consume line'." assert move_line.location_dest_id.id == source_location_id, "Destination Location is not correspond in 'To consume line'." -- - I consume raw materials and put one material in scrap location due to waste it. -- - !python {model: mrp.production}: | - scrap_location_ids = self.pool.get('stock.location').search(cr, uid, [('scrap_location','=',True)]) - scrap_location_id = scrap_location_ids[0] - order = self.browse(cr, uid, ref("mrp_production_test1")) - for move in order.move_lines: - if move.product_id.id == ref("product.product_product_6"): - move.action_scrap(5.0, scrap_location_id) - I check details of an Internal Shipment after confirmed production order to bring components in Raw Materials Location. - @@ -110,11 +94,6 @@ procurement_ids = procurement.search(cr, uid, [('move_id','=',move_line.id)]) assert procurement_ids, "Procurement should be created for shipment line of raw materials." shipment_procurement = procurement.browse(cr, uid, procurement_ids[0], context=context) - # procurement state should be `confirmed` at this stage, except if mrp_jit is installed, in which - # case it could already be in `running` or `exception` state (not enough stock) - expected_states = ('confirmed', 'running', 'exception') - assert shipment_procurement.state in expected_states, 'Procurement state is `%s` for %s, expected one of %s' % \ - (shipment_procurement.state, shipment_procurement.product_id.name, expected_states) assert shipment_procurement.date_planned == date_planned, "Planned date is not correspond in procurement." assert shipment_procurement.product_id.id == order_line.product_id.id, "Product is not correspond in procurement." assert shipment_procurement.product_qty == order_line.product_qty, "Qty is not correspond in procurement." @@ -171,7 +150,17 @@ order = self.browse(cr, uid, ref("mrp_production_test1")) assert order.state == 'in_production', 'Production order should be in production State.' - + I consume raw materials and put one material in scrap location due to waste it. +- + !python {model: mrp.production}: | + scrap_location_ids = self.pool.get('stock.location').search(cr, uid, [('scrap_location','=',True)]) + scrap_location_id = scrap_location_ids[0] + order = self.browse(cr, uid, ref("mrp_production_test1")) + for move in order.move_lines: move.action_consume(move.product_qty) + if move.product_id.id == ref("product.product_product_6"): + move.action_scrap(5.0, scrap_location_id) +- I produce product. - !python {model: mrp.product.produce}: | @@ -189,10 +178,7 @@ order = self.browse(cr, uid, ref("mrp_production_test1")) assert order.state == 'done', "Production order should be closed." - - I check Total Costs at End of Production as a manager. -- - !context - uid: 'res_users_mrp_manager' + I check Total Costs at End of Production. - !python {model: mrp.production}: | order = self.browse(cr, uid, ref("mrp_production_test1")) @@ -226,9 +212,6 @@ assert line.product_uom_id.id == wc.product_id.uom_id.id, "UOM is not correspond." - I print a "BOM Structure". -- - !context - uid: 'res_users_mrp_user' - !python {model: mrp.production}: | import netsvc, tools, os diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py index c9ac13b3ce02b..f147147d303d9 100644 --- a/addons/mrp/wizard/change_production_qty.py +++ b/addons/mrp/wizard/change_production_qty.py @@ -1,107 +1,99 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -from openerp.tools.translate import _ -import openerp.addons.decimal_precision as dp - -class change_production_qty(osv.osv_memory): - _name = 'change.production.qty' - _description = 'Change Quantity of Products' - - _columns = { - 'product_qty': fields.float('Product Qty', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), - } - - def default_get(self, cr, uid, fields, context=None): - """ To get default values for the object. - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param fields: List of fields for which we want default values - @param context: A standard dictionary - @return: A dictionary which of fields with values. - """ - if context is None: - context = {} - res = super(change_production_qty, self).default_get(cr, uid, fields, context=context) - prod_obj = self.pool.get('mrp.production') - prod = prod_obj.browse(cr, uid, context.get('active_id'), context=context) - if 'product_qty' in fields: - res.update({'product_qty': prod.product_qty}) - return res - - def _update_product_to_produce(self, cr, uid, prod, qty, context=None): - move_lines_obj = self.pool.get('stock.move') - for m in prod.move_created_ids: - move_lines_obj.write(cr, uid, [m.id], {'product_qty': qty}) - - def change_prod_qty(self, cr, uid, ids, context=None): - """ - Changes the Quantity of Product. - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param ids: List of IDs selected - @param context: A standard dictionary - @return: - """ - record_id = context and context.get('active_id',False) - assert record_id, _('Active Id not found') - prod_obj = self.pool.get('mrp.production') - bom_obj = self.pool.get('mrp.bom') - move_obj = self.pool.get('stock.move') - uom_obj = self.pool.get('product.uom') - for wiz_qty in self.browse(cr, uid, ids, context=context): - prod = prod_obj.browse(cr, uid, record_id, context=context) - prod_obj.write(cr, uid, [prod.id], {'product_qty': wiz_qty.product_qty}) - prod_obj.action_compute(cr, uid, [prod.id]) - - for move in prod.move_lines: - bom_point = prod.bom_id - bom_id = prod.bom_id.id - if not bom_point: - bom_id = bom_obj._bom_find(cr, uid, prod.product_id.id, prod.product_uom.id) - if not bom_id: - raise osv.except_osv(_('Error!'), _("Cannot find bill of material for this product.")) - prod_obj.write(cr, uid, [prod.id], {'bom_id': bom_id}) - bom_point = bom_obj.browse(cr, uid, [bom_id])[0] - - if not bom_id: - raise osv.except_osv(_('Error!'), _("Cannot find bill of material for this product.")) - - factor = uom_obj._compute_qty(cr, uid, prod.product_uom.id, prod.product_qty, bom_point.product_uom.id) - product_details, workcenter_details = \ - bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, []) - product_move = dict((mv.product_id.id, mv.id) for mv in prod.picking_id.move_lines) - for r in product_details: - if r['product_id'] == move.product_id.id: - move_obj.write(cr, uid, [move.id], {'product_qty': r['product_qty']}) - if r['product_id'] in product_move: - move_obj.write(cr, uid, [product_move[r['product_id']]], {'product_qty': r['product_qty']}) - if prod.move_prod_id: - move_obj.write(cr, uid, [prod.move_prod_id.id], {'product_qty' : wiz_qty.product_qty}) - self._update_product_to_produce(cr, uid, prod, wiz_qty.product_qty, context=context) - return {} - -change_production_qty() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +from openerp.tools.translate import _ +import openerp.addons.decimal_precision as dp + +class change_production_qty(osv.osv_memory): + _name = 'change.production.qty' + _description = 'Change Quantity of Products' + _columns = {'product_qty': fields.float('Product Qty', digits_compute=dp.get_precision('Product Unit of Measure'), required=True)} + + def default_get(self, cr, uid, fields, context = None): + """ To get default values for the object. + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param fields: List of fields for which we want default values + @param context: A standard dictionary + @return: A dictionary which of fields with values. + """ + if context is None: + context = {} + res = super(change_production_qty, self).default_get(cr, uid, fields, context=context) + prod_obj = self.pool.get('mrp.production') + prod = prod_obj.browse(cr, uid, context.get('active_id'), context=context) + if 'product_qty' in fields: + res.update({'product_qty': prod.product_qty}) + return res + + def _update_product_to_produce(self, cr, uid, prod, qty, context = None): + move_lines_obj = self.pool.get('stock.move') + for m in prod.move_created_ids: + move_lines_obj.write(cr, uid, [m.id], {'product_qty': qty}) + + def change_prod_qty(self, cr, uid, ids, context = None): + """ + Changes the Quantity of Product. + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param ids: List of IDs selected + @param context: A standard dictionary + @return: + """ + record_id = context and context.get('active_id', False) + raise record_id or AssertionError(_('Active Id not found')) + prod_obj = self.pool.get('mrp.production') + bom_obj = self.pool.get('mrp.bom') + move_obj = self.pool.get('stock.move') + for wiz_qty in self.browse(cr, uid, ids, context=context): + prod = prod_obj.browse(cr, uid, record_id, context=context) + prod_obj.write(cr, uid, [prod.id], {'product_qty': wiz_qty.product_qty}) + prod_obj.action_compute(cr, uid, [prod.id]) + for move in prod.move_lines: + bom_point = prod.bom_id + bom_id = prod.bom_id.id + if not bom_point: + bom_id = bom_obj._bom_find(cr, uid, prod.product_id.id, prod.product_uom.id) + if not bom_id: + raise osv.except_osv(_('Error!'), _('Cannot find bill of material for this product.')) + prod_obj.write(cr, uid, [prod.id], {'bom_id': bom_id}) + bom_point = bom_obj.browse(cr, uid, [bom_id])[0] + if not bom_id: + raise osv.except_osv(_('Error!'), _('Cannot find bill of material for this product.')) + factor = prod.product_qty * prod.product_uom.factor / bom_point.product_uom.factor + product_details, workcenter_details = bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, []) + product_move = dict(((mv.product_id.id, mv.id) for mv in prod.picking_id.move_lines)) + for r in product_details: + if r['product_id'] == move.product_id.id: + move_obj.write(cr, uid, [move.id], {'product_qty': r['product_qty']}) + if r['product_id'] in product_move: + move_obj.write(cr, uid, [product_move[r['product_id']]], {'product_qty': r['product_qty']}) + + if prod.move_prod_id: + move_obj.write(cr, uid, [prod.move_prod_id.id], {'product_qty': wiz_qty.product_qty}) + self._update_product_to_produce(cr, uid, prod, wiz_qty.product_qty, context=context) + + return {} + + +change_production_qty() \ No newline at end of file diff --git a/addons/mrp/wizard/mrp_price.py b/addons/mrp/wizard/mrp_price.py index 08506ccf0e203..d9eed25b972a4 100644 --- a/addons/mrp/wizard/mrp_price.py +++ b/addons/mrp/wizard/mrp_price.py @@ -1,57 +1,48 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv - -class mrp_price(osv.osv_memory): - _name = 'mrp.product_price' - _description = 'Product Price' - _columns = { - 'number': fields.integer('Quantity', required=True, help="Specify quantity of products to produce or buy. Report of Cost structure will be displayed base on this quantity."), - } - _defaults = { - 'number': 1, - } - - def print_report(self, cr, uid, ids, context=None): - """ To print the report of Product cost structure - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param context: A standard dictionary - @return : Report - """ - if context is None: - context = {} - datas = {'ids' : context.get('active_ids',[])} - res = self.read(cr, uid, ids, ['number']) - res = res and res[0] or {} - datas['form'] = res - - return { - 'type' : 'ir.actions.report.xml', - 'report_name':'product.price', - 'datas' : datas, - } - -mrp_price() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv + +class mrp_price(osv.osv_memory): + _name = 'mrp.product_price' + _description = 'Product Price' + _columns = {'number': fields.integer('Quantity', required=True, help='Specify quantity of products to produce or buy. Report of Cost structure will be displayed base on this quantity.')} + _defaults = {'number': 1} + + def print_report(self, cr, uid, ids, context = None): + """ To print the report of Product cost structure + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param context: A standard dictionary + @return : Report + """ + if context is None: + context = {} + datas = {'ids': context.get('active_ids', [])} + res = self.read(cr, uid, ids, ['number']) + res = res and res[0] or {} + datas['form'] = res + return {'type': 'ir.actions.report.xml', + 'report_name': 'product.price', + 'datas': datas} + + +mrp_price() \ No newline at end of file diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py index 3a3c5961d1e83..09d24e7bfd7d7 100644 --- a/addons/mrp/wizard/mrp_product_produce.py +++ b/addons/mrp/wizard/mrp_product_produce.py @@ -1,72 +1,60 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv -import openerp.addons.decimal_precision as dp - -class mrp_product_produce(osv.osv_memory): - _name = "mrp.product.produce" - _description = "Product Produce" - - _columns = { - 'product_qty': fields.float('Select Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), - 'mode': fields.selection([('consume_produce', 'Consume & Produce'), - ('consume', 'Consume Only')], 'Mode', required=True, - help="'Consume only' mode will only consume the products with the quantity selected.\n" - "'Consume & Produce' mode will consume as well as produce the products with the quantity selected " - "and it will finish the production order when total ordered quantities are produced."), - } - - def _get_product_qty(self, cr, uid, context=None): - """ To obtain product quantity - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param context: A standard dictionary - @return: Quantity - """ - if context is None: - context = {} - prod = self.pool.get('mrp.production').browse(cr, uid, - context['active_id'], context=context) - done = 0.0 - for move in prod.move_created_ids2: - if move.product_id == prod.product_id: - if not move.scrapped: - done += move.product_qty - return (prod.product_qty - done) or prod.product_qty - - _defaults = { - 'product_qty': _get_product_qty, - 'mode': lambda *x: 'consume_produce' - } - - def do_produce(self, cr, uid, ids, context=None): - production_id = context.get('active_id', False) - assert production_id, "Production Id should be specified in context as a Active ID." - data = self.browse(cr, uid, ids[0], context=context) - self.pool.get('mrp.production').action_produce(cr, uid, production_id, - data.product_qty, data.mode, context=context) - return {} - -mrp_product_produce() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv +import openerp.addons.decimal_precision as dp + +class mrp_product_produce(osv.osv_memory): + _name = 'mrp.product.produce' + _description = 'Product Produce' + _columns = {'product_qty': fields.float('Select Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), + 'mode': fields.selection([('consume_produce', 'Consume & Produce'), ('consume', 'Consume Only')], 'Mode', required=True, help="'Consume only' mode will only consume the products with the quantity selected.\n'Consume & Produce' mode will consume as well as produce the products with the quantity selected and it will finish the production order when total ordered quantities are produced.")} + + def _get_product_qty(self, cr, uid, context = None): + """ To obtain product quantity + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param context: A standard dictionary + @return: Quantity + """ + if context is None: + context = {} + prod = self.pool.get('mrp.production').browse(cr, uid, context['active_id'], context=context) + done = 0.0 + for move in prod.move_created_ids2: + if move.product_id == prod.product_id: + if not move.scrapped: + done += move.product_qty + + return prod.product_qty - done or prod.product_qty + + _defaults = {'product_qty': _get_product_qty, + 'mode': lambda *x: 'consume_produce'} + + def do_produce(self, cr, uid, ids, context = None): + production_id = context.get('active_id', False) + raise production_id or AssertionError('Production Id should be specified in context as a Active ID.') + data = self.browse(cr, uid, ids[0], context=context) + self.pool.get('mrp.production').action_produce(cr, uid, production_id, data.product_qty, data.mode, context=context) + return {} + + +mrp_product_produce() \ No newline at end of file diff --git a/addons/mrp/wizard/mrp_workcenter_load.py b/addons/mrp/wizard/mrp_workcenter_load.py index 265c6ddcea04f..9ac04beac2894 100644 --- a/addons/mrp/wizard/mrp_workcenter_load.py +++ b/addons/mrp/wizard/mrp_workcenter_load.py @@ -1,56 +1,48 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields, osv - -class mrp_workcenter_load(osv.osv_memory): - _name = 'mrp.workcenter.load' - _description = 'Work Center Load' - - _columns = { - 'time_unit': fields.selection([('day', 'Day by day'),('week', 'Per week'),('month', 'Per month')],'Type of period', required=True), - 'measure_unit': fields.selection([('hours', 'Amount in hours'),('cycles', 'Amount in cycles')],'Amount measuring unit', required=True), - } - - def print_report(self, cr, uid, ids, context=None): - """ To print the report of Work Center Load - @param self: The object pointer. - @param cr: A database cursor - @param uid: ID of the user currently logged in - @param context: A standard dictionary - @return : Report - """ - if context is None: - context = {} - datas = {'ids' : context.get('active_ids',[])} - res = self.read(cr, uid, ids, ['time_unit','measure_unit']) - res = res and res[0] or {} - datas['form'] = res - - return { - 'type' : 'ir.actions.report.xml', - 'report_name':'mrp.workcenter.load', - 'datas' : datas, - } - -mrp_workcenter_load() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields, osv + +class mrp_workcenter_load(osv.osv_memory): + _name = 'mrp.workcenter.load' + _description = 'Work Center Load' + _columns = {'time_unit': fields.selection([('day', 'Day by day'), ('week', 'Per week'), ('month', 'Per month')], 'Type of period', required=True), + 'measure_unit': fields.selection([('hours', 'Amount in hours'), ('cycles', 'Amount in cycles')], 'Amount measuring unit', required=True)} + + def print_report(self, cr, uid, ids, context = None): + """ To print the report of Work Center Load + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param context: A standard dictionary + @return : Report + """ + if context is None: + context = {} + datas = {'ids': context.get('active_ids', [])} + res = self.read(cr, uid, ids, ['time_unit', 'measure_unit']) + res = res and res[0] or {} + datas['form'] = res + return {'type': 'ir.actions.report.xml', + 'report_name': 'mrp.workcenter.load', + 'datas': datas} + + +mrp_workcenter_load() \ No newline at end of file diff --git a/addons/mrp_byproduct/images/bom_byproduct.jpeg b/addons/mrp_byproduct/images/bom_byproduct.jpeg new file mode 100644 index 0000000000000..ea93a5fd6b57d Binary files /dev/null and b/addons/mrp_byproduct/images/bom_byproduct.jpeg differ diff --git a/addons/mrp_byproduct/mrp_byproduct.py b/addons/mrp_byproduct/mrp_byproduct.py index 605e80e622349..8f3bbdafe8e12 100644 --- a/addons/mrp_byproduct/mrp_byproduct.py +++ b/addons/mrp_byproduct/mrp_byproduct.py @@ -1,165 +1,154 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields -from openerp.osv import osv -import openerp.addons.decimal_precision as dp -from openerp.tools.translate import _ - -class mrp_subproduct(osv.osv): - _name = 'mrp.subproduct' - _description = 'Byproduct' - _columns={ - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'product_qty': fields.float('Product Qty', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), - 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True), - 'subproduct_type': fields.selection([('fixed','Fixed'),('variable','Variable')], 'Quantity Type', required=True, help="Define how the quantity of byproducts will be set on the production orders using this BoM.\ - 'Fixed' depicts a situation where the quantity of created byproduct is always equal to the quantity set on the BoM, regardless of how many are created in the production order.\ - By opposition, 'Variable' means that the quantity will be computed as\ - '(quantity of byproduct set on the BoM / quantity of manufactured product set on the BoM * quantity of manufactured product in the production order.)'"), - 'bom_id': fields.many2one('mrp.bom', 'BoM'), - } - _defaults={ - 'subproduct_type': 'variable', - 'product_qty': lambda *a: 1.0, - } - - def onchange_product_id(self, cr, uid, ids, product_id, context=None): - """ Changes UoM if product_id changes. - @param product_id: Changed product_id - @return: Dictionary of changed values - """ - if product_id: - prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - v = {'product_uom': prod.uom_id.id} - return {'value': v} - return {} - - def onchange_uom(self, cr, uid, ids, product_id, product_uom, context=None): - res = {'value':{}} - if not product_uom or not product_id: - return res - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) - if uom.category_id.id != product.uom_id.category_id.id: - res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} - res['value'].update({'product_uom': product.uom_id.id}) - return res - -mrp_subproduct() - -class mrp_bom(osv.osv): - _name = 'mrp.bom' - _description = 'Bill of Material' - _inherit='mrp.bom' - - _columns={ - 'sub_products':fields.one2many('mrp.subproduct', 'bom_id', 'Byproducts'), - } - -mrp_bom() - -class mrp_production(osv.osv): - _description = 'Production' - _inherit= 'mrp.production' - - - def action_confirm(self, cr, uid, ids, context=None): - """ Confirms production order and calculates quantity based on subproduct_type. - @return: Newly generated picking Id. - """ - picking_id = super(mrp_production,self).action_confirm(cr, uid, ids, context=context) - product_uom_obj = self.pool.get('product.uom') - for production in self.browse(cr, uid, ids): - source = production.product_id.property_stock_production.id - if not production.bom_id: - continue - for sub_product in production.bom_id.sub_products: - product_uom_factor = product_uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, production.bom_id.product_uom.id) - qty1 = sub_product.product_qty - qty2 = production.product_uos and production.product_uos_qty or False - product_uos_factor = 0.0 - if qty2 and production.bom_id.product_uos.id: - product_uos_factor = product_uom_obj._compute_qty(cr, uid, production.product_uos.id, production.product_uos_qty, production.bom_id.product_uos.id) - if sub_product.subproduct_type == 'variable': - if production.product_qty: - qty1 *= product_uom_factor / (production.bom_id.product_qty or 1.0) - if production.product_uos_qty: - qty2 *= product_uos_factor / (production.bom_id.product_uos_qty or 1.0) - data = { - 'name': 'PROD:'+production.name, - 'date': production.date_planned, - 'product_id': sub_product.product_id.id, - 'product_qty': qty1, - 'product_uom': sub_product.product_uom.id, - 'product_uos_qty': qty2, - 'product_uos': production.product_uos and production.product_uos.id or False, - 'location_id': source, - 'location_dest_id': production.location_dest_id.id, - 'move_dest_id': production.move_prod_id.id, - 'state': 'waiting', - 'production_id': production.id - } - self.pool.get('stock.move').create(cr, uid, data) - return picking_id - - def _get_subproduct_factor(self, cr, uid, production_id, move_id=None, context=None): - """Compute the factor to compute the qty of procucts to produce for the given production_id. By default, - it's always equal to the quantity encoded in the production order or the production wizard, but with - the module mrp_byproduct installed it can differ for byproducts having type 'variable'. - :param production_id: ID of the mrp.order - :param move_id: ID of the stock move that needs to be produced. Identify the product to produce. - :return: The factor to apply to the quantity that we should produce for the given production order and stock move. - """ - sub_obj = self.pool.get('mrp.subproduct') - move_obj = self.pool.get('stock.move') - production_obj = self.pool.get('mrp.production') - production_browse = production_obj.browse(cr, uid, production_id, context=context) - move_browse = move_obj.browse(cr, uid, move_id, context=context) - subproduct_factor = 1 - sub_id = sub_obj.search(cr, uid,[('product_id', '=', move_browse.product_id.id),('bom_id', '=', production_browse.bom_id.id), ('subproduct_type', '=', 'variable')], context=context) - if sub_id: - subproduct_record = sub_obj.browse(cr ,uid, sub_id[0], context=context) - if subproduct_record.bom_id.product_qty: - subproduct_factor = subproduct_record.product_qty / subproduct_record.bom_id.product_qty - return subproduct_factor - return super(mrp_production, self)._get_subproduct_factor(cr, uid, production_id, move_id, context=context) - -mrp_production() - -class change_production_qty(osv.osv_memory): - _inherit = 'change.production.qty' - - def _update_product_to_produce(self, cr, uid, prod, qty, context=None): - bom_obj = self.pool.get('mrp.bom') - move_lines_obj = self.pool.get('stock.move') - prod_obj = self.pool.get('mrp.production') - for m in prod.move_created_ids: - if m.product_id.id == prod.product_id.id: - move_lines_obj.write(cr, uid, [m.id], {'product_qty': qty}) - else: - for sub_product_line in prod.bom_id.sub_products: - if sub_product_line.product_id.id == m.product_id.id: - factor = prod_obj._get_subproduct_factor(cr, uid, prod.id, m.id, context=context) - subproduct_qty = sub_product_line.subproduct_type == 'variable' and qty * factor or sub_product_line.product_qty - move_lines_obj.write(cr, uid, [m.id], {'product_qty': subproduct_qty}) - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields +from openerp.osv import osv +import openerp.addons.decimal_precision as dp +from openerp.tools.translate import _ + +class mrp_subproduct(osv.osv): + _name = 'mrp.subproduct' + _description = 'Byproduct' + _columns = {'product_id': fields.many2one('product.product', 'Product', required=True), + 'product_qty': fields.float('Product Qty', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), + 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True), + 'subproduct_type': fields.selection([('fixed', 'Fixed'), ('variable', 'Variable')], 'Quantity Type', required=True, help="Define how the quantity of byproducts will be set on the production orders using this BoM. 'Fixed' depicts a situation where the quantity of created byproduct is always equal to the quantity set on the BoM, regardless of how many are created in the production order. By opposition, 'Variable' means that the quantity will be computed as '(quantity of byproduct set on the BoM / quantity of manufactured product set on the BoM * quantity of manufactured product in the production order.)'"), + 'bom_id': fields.many2one('mrp.bom', 'BoM')} + _defaults = {'subproduct_type': 'variable', + 'product_qty': lambda *a: 1.0} + + def onchange_product_id(self, cr, uid, ids, product_id, context = None): + """ Changes UoM if product_id changes. + @param product_id: Changed product_id + @return: Dictionary of changed values + """ + if product_id: + prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + v = {'product_uom': prod.uom_id.id} + return {'value': v} + return {} + + def onchange_uom(self, cr, uid, ids, product_id, product_uom, context = None): + res = {'value': {}} + if not product_uom or not product_id: + return res + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), + 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} + res['value'].update({'product_uom': product.uom_id.id}) + return res + + +mrp_subproduct() + +class mrp_bom(osv.osv): + _name = 'mrp.bom' + _description = 'Bill of Material' + _inherit = 'mrp.bom' + _columns = {'sub_products': fields.one2many('mrp.subproduct', 'bom_id', 'Byproducts')} + + +mrp_bom() + +class mrp_production(osv.osv): + _description = 'Production' + _inherit = 'mrp.production' + + def action_confirm(self, cr, uid, ids): + """ Confirms production order and calculates quantity based on subproduct_type. + @return: Newly generated picking Id. + """ + picking_id = super(mrp_production, self).action_confirm(cr, uid, ids) + product_uom_obj = self.pool.get('product.uom') + for production in self.browse(cr, uid, ids): + source = production.product_id.property_stock_production.id + if not production.bom_id: + continue + for sub_product in production.bom_id.sub_products: + product_uom_factor = product_uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, production.bom_id.product_uom.id) + qty1 = sub_product.product_qty + qty2 = production.product_uos and production.product_uos_qty or False + product_uos_factor = 0.0 + if qty2 and production.bom_id.product_uos.id: + product_uos_factor = product_uom_obj._compute_qty(cr, uid, production.product_uos.id, production.product_uos_qty, production.bom_id.product_uos.id) + if sub_product.subproduct_type == 'variable': + if production.product_qty: + qty1 *= product_uom_factor / (production.bom_id.product_qty or 1.0) + if production.product_uos_qty: + qty2 *= product_uos_factor / (production.bom_id.product_uos_qty or 1.0) + data = {'name': 'PROD:' + production.name, + 'date': production.date_planned, + 'product_id': sub_product.product_id.id, + 'product_qty': qty1, + 'product_uom': sub_product.product_uom.id, + 'product_uos_qty': qty2, + 'product_uos': production.product_uos and production.product_uos.id or False, + 'location_id': source, + 'location_dest_id': production.location_dest_id.id, + 'move_dest_id': production.move_prod_id.id, + 'state': 'waiting', + 'production_id': production.id} + self.pool.get('stock.move').create(cr, uid, data) + + return picking_id + + def _get_subproduct_factor(self, cr, uid, production_id, move_id = None, context = None): + """Compute the factor to compute the qty of procucts to produce for the given production_id. By default, + it's always equal to the quantity encoded in the production order or the production wizard, but with + the module mrp_byproduct installed it can differ for byproducts having type 'variable'. + :param production_id: ID of the mrp.order + :param move_id: ID of the stock move that needs to be produced. Identify the product to produce. + :return: The factor to apply to the quantity that we should produce for the given production order and stock move. + """ + sub_obj = self.pool.get('mrp.subproduct') + move_obj = self.pool.get('stock.move') + production_obj = self.pool.get('mrp.production') + production_browse = production_obj.browse(cr, uid, production_id, context=context) + move_browse = move_obj.browse(cr, uid, move_id, context=context) + subproduct_factor = 1 + sub_id = sub_obj.search(cr, uid, [('product_id', '=', move_browse.product_id.id), ('bom_id', '=', production_browse.bom_id.id), ('subproduct_type', '=', 'variable')], context=context) + if sub_id: + subproduct_record = sub_obj.browse(cr, uid, sub_id[0], context=context) + if subproduct_record.bom_id.product_qty: + subproduct_factor = subproduct_record.product_qty / subproduct_record.bom_id.product_qty + return subproduct_factor + return super(mrp_production, self)._get_subproduct_factor(cr, uid, production_id, move_id, context=context) + + +mrp_production() + +class change_production_qty(osv.osv_memory): + _inherit = 'change.production.qty' + + def _update_product_to_produce(self, cr, uid, prod, qty, context = None): + bom_obj = self.pool.get('mrp.bom') + move_lines_obj = self.pool.get('stock.move') + prod_obj = self.pool.get('mrp.production') + for m in prod.move_created_ids: + if m.product_id.id == prod.product_id.id: + move_lines_obj.write(cr, uid, [m.id], {'product_qty': qty}) + else: + for sub_product_line in prod.bom_id.sub_products: + if sub_product_line.product_id.id == m.product_id.id: + factor = prod_obj._get_subproduct_factor(cr, uid, prod.id, m.id, context=context) + subproduct_qty = sub_product_line.subproduct_type == 'variable' and qty * factor or sub_product_line.product_qty + move_lines_obj.write(cr, uid, [m.id], {'product_qty': subproduct_qty}) \ No newline at end of file diff --git a/addons/mrp_operations/__openerp__.py b/addons/mrp_operations/__openerp__.py index 12d86e0b53271..7fedb426edd8b 100644 --- a/addons/mrp_operations/__openerp__.py +++ b/addons/mrp_operations/__openerp__.py @@ -70,7 +70,7 @@ 'mrp_operations_demo.yml' ], 'test': [ - 'test/workcenter_operations.yml', +# 'test/workcenter_operations.yml', ], 'installable': True, 'auto_install': False, diff --git a/addons/mrp_operations/images/work_order.jpeg b/addons/mrp_operations/images/work_order.jpeg new file mode 100644 index 0000000000000..d59ce81dc81fa Binary files /dev/null and b/addons/mrp_operations/images/work_order.jpeg differ diff --git a/addons/mrp_operations/images/work_order_analysis.jpeg b/addons/mrp_operations/images/work_order_analysis.jpeg new file mode 100644 index 0000000000000..ac229783efbda Binary files /dev/null and b/addons/mrp_operations/images/work_order_analysis.jpeg differ diff --git a/addons/mrp_operations/images/work_order_planning.jpeg b/addons/mrp_operations/images/work_order_planning.jpeg new file mode 100644 index 0000000000000..ef26d754ec6fc Binary files /dev/null and b/addons/mrp_operations/images/work_order_planning.jpeg differ diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py index f2b288733ff19..ebf2b10b88b87 100644 --- a/addons/mrp_operations/mrp_operations.py +++ b/addons/mrp_operations/mrp_operations.py @@ -1,572 +1,535 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv import fields -from openerp.osv import osv -from openerp import netsvc -import time -from datetime import datetime -from openerp.tools.translate import _ - -#---------------------------------------------------------- -# Work Centers -#---------------------------------------------------------- -# capacity_hour : capacity per hour. default: 1.0. -# Eg: If 5 concurrent operations at one time: capacity = 5 (because 5 employees) -# unit_per_cycle : how many units are produced for one cycle - -class stock_move(osv.osv): - _inherit = 'stock.move' - _columns = { - 'move_dest_id_lines': fields.one2many('stock.move','move_dest_id', 'Children Moves') - } - - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - default.update({ - 'move_dest_id_lines': [], - }) - return super(stock_move, self).copy(cr, uid, id, default, context) - -stock_move() - -class mrp_production_workcenter_line(osv.osv): - - def _get_date_end(self, cr, uid, ids, field_name, arg, context=None): - """ Finds ending date. - @return: Dictionary of values. - """ - ops = self.browse(cr, uid, ids, context=context) - date_and_hours_by_cal = [(op.date_planned, op.hour, op.workcenter_id.calendar_id.id) for op in ops if op.date_planned] - - intervals = self.pool.get('resource.calendar').interval_get_multi(cr, uid, date_and_hours_by_cal) - - res = {} - for op in ops: - res[op.id] = False - if op.date_planned: - i = intervals.get((op.date_planned, op.hour, op.workcenter_id.calendar_id.id)) - if i: - res[op.id] = i[-1][1].strftime('%Y-%m-%d %H:%M:%S') - else: - res[op.id] = op.date_planned - return res - - def onchange_production_id(self, cr, uid, ids, production_id, context=None): - if not production_id: - return {} - production = self.pool.get('mrp.production').browse(cr, uid, production_id, context=None) - result = { - 'product': production.product_id.id, - 'qty': production.product_qty, - 'uom': production.product_uom.id, - } - return {'value': result} - - _inherit = 'mrp.production.workcenter.line' - _order = "sequence, date_planned" - - _columns = { - 'state': fields.selection([('draft','Draft'),('cancel','Cancelled'),('pause','Pending'),('startworking', 'In Progress'),('done','Finished')],'Status', readonly=True, - help="* When a work order is created it is set in 'Draft' status.\n" \ - "* When user sets work order in start mode that time it will be set in 'In Progress' status.\n" \ - "* When work order is in running mode, during that time if user wants to stop or to make changes in order then can set in 'Pending' status.\n" \ - "* When the user cancels the work order it will be set in 'Canceled' status.\n" \ - "* When order is completely processed that time it is set in 'Finished' status."), - 'date_planned': fields.datetime('Scheduled Date', select=True), - 'date_planned_end': fields.function(_get_date_end, string='End Date', type='datetime'), - 'date_start': fields.datetime('Start Date'), - 'date_finished': fields.datetime('End Date'), - 'delay': fields.float('Working Hours',help="The elapsed time between operation start and stop in this Work Center",readonly=True), - 'production_state':fields.related('production_id','state', - type='selection', - selection=[('draft','Draft'),('picking_except', 'Picking Exception'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')], - string='Production Status', readonly=True), - 'product':fields.related('production_id','product_id',type='many2one',relation='product.product',string='Product', - readonly=True), - 'qty':fields.related('production_id','product_qty',type='float',string='Qty',readonly=True, store=True), - 'uom':fields.related('production_id','product_uom',type='many2one',relation='product.uom',string='Unit of Measure',readonly=True), - } - - _defaults = { - 'state': 'draft', - 'delay': 0.0, - 'production_state': 'draft' - } - - def modify_production_order_state(self, cr, uid, ids, action): - """ Modifies production order state if work order state is changed. - @param action: Action to perform. - @return: Nothing - """ - wf_service = netsvc.LocalService("workflow") - prod_obj_pool = self.pool.get('mrp.production') - oper_obj = self.browse(cr, uid, ids)[0] - prod_obj = oper_obj.production_id - if action == 'start': - if prod_obj.state =='confirmed': - prod_obj_pool.force_production(cr, uid, [prod_obj.id]) - wf_service.trg_validate(uid, 'mrp.production', prod_obj.id, 'button_produce', cr) - elif prod_obj.state =='ready': - wf_service.trg_validate(uid, 'mrp.production', prod_obj.id, 'button_produce', cr) - elif prod_obj.state =='in_production': - return - else: - raise osv.except_osv(_('Error!'),_('Manufacturing order cannot be started in state "%s"!') % (prod_obj.state,)) - else: - oper_ids = self.search(cr,uid,[('production_id','=',prod_obj.id)]) - obj = self.browse(cr,uid,oper_ids) - flag = True - for line in obj: - if line.state != 'done': - flag = False - if flag: - for production in prod_obj_pool.browse(cr, uid, [prod_obj.id], context= None): - if production.move_lines or production.move_created_ids: - prod_obj_pool.action_produce(cr,uid, production.id, production.product_qty, 'consume_produce', context = None) - wf_service.trg_validate(uid, 'mrp.production', oper_obj.production_id.id, 'button_produce_done', cr) - return - - def write(self, cr, uid, ids, vals, context=None, update=True): - result = super(mrp_production_workcenter_line, self).write(cr, uid, ids, vals, context=context) - prod_obj = self.pool.get('mrp.production') - if vals.get('date_planned', False) and update: - for prod in self.browse(cr, uid, ids, context=context): - if prod.production_id.workcenter_lines: - dstart = min(vals['date_planned'], prod.production_id.workcenter_lines[0]['date_planned']) - prod_obj.write(cr, uid, [prod.production_id.id], {'date_start':dstart}, context=context, mini=False) - return result - - def action_draft(self, cr, uid, ids, context=None): - """ Sets state to draft. - @return: True - """ - return self.write(cr, uid, ids, {'state': 'draft'}, context=context) - - def action_start_working(self, cr, uid, ids, context=None): - """ Sets state to start working and writes starting date. - @return: True - """ - self.modify_production_order_state(cr, uid, ids, 'start') - self.write(cr, uid, ids, {'state':'startworking', 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context) - return True - - def action_done(self, cr, uid, ids, context=None): - """ Sets state to done, writes finish date and calculates delay. - @return: True - """ - delay = 0.0 - date_now = time.strftime('%Y-%m-%d %H:%M:%S') - obj_line = self.browse(cr, uid, ids[0]) - - date_start = datetime.strptime(obj_line.date_start,'%Y-%m-%d %H:%M:%S') - date_finished = datetime.strptime(date_now,'%Y-%m-%d %H:%M:%S') - delay += (date_finished-date_start).days * 24 - delay += (date_finished-date_start).seconds / float(60*60) - - self.write(cr, uid, ids, {'state':'done', 'date_finished': date_now,'delay':delay}, context=context) - self.modify_production_order_state(cr,uid,ids,'done') - return True - - def action_cancel(self, cr, uid, ids, context=None): - """ Sets state to cancel. - @return: True - """ - return self.write(cr, uid, ids, {'state':'cancel'}, context=context) - - def action_pause(self, cr, uid, ids, context=None): - """ Sets state to pause. - @return: True - """ - return self.write(cr, uid, ids, {'state':'pause'}, context=context) - - def action_resume(self, cr, uid, ids, context=None): - """ Sets state to startworking. - @return: True - """ - return self.write(cr, uid, ids, {'state':'startworking'}, context=context) - - -class mrp_production(osv.osv): - _inherit = 'mrp.production' - _columns = { - 'allow_reorder': fields.boolean('Free Serialisation', help="Check this to be able to move independently all production orders, without moving dependent ones."), - } - - def _production_date_end(self, cr, uid, ids, prop, unknow_none, context=None): - """ Calculates planned end date of production order. - @return: Dictionary of values - """ - result = {} - for prod in self.browse(cr, uid, ids, context=context): - result[prod.id] = prod.date_planned - for line in prod.workcenter_lines: - result[prod.id] = max(line.date_planned_end, result[prod.id]) - return result - - def action_production_end(self, cr, uid, ids, context=None): - """ Finishes work order if production order is done. - @return: Super method - """ - obj = self.browse(cr, uid, ids)[0] - wf_service = netsvc.LocalService("workflow") - for workcenter_line in obj.workcenter_lines: - if workcenter_line.state == 'draft': - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_start_working', cr) - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_done', cr) - return super(mrp_production,self).action_production_end(cr, uid, ids, context=context) - - def action_in_production(self, cr, uid, ids, context=None): - """ Changes state to In Production and writes starting date. - @return: True - """ - obj = self.browse(cr, uid, ids)[0] - workcenter_pool = self.pool.get('mrp.production.workcenter.line') - wf_service = netsvc.LocalService("workflow") - for prod in self.browse(cr, uid, ids): - if prod.workcenter_lines: - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', prod.workcenter_lines[0].id, 'button_start_working', cr) - return super(mrp_production,self).action_in_production(cr, uid, ids, context=context) - - def action_cancel(self, cr, uid, ids, context=None): - """ Cancels work order if production order is canceled. - @return: Super method - """ - obj = self.browse(cr, uid, ids,context=context)[0] - wf_service = netsvc.LocalService("workflow") - for workcenter_line in obj.workcenter_lines: - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_cancel', cr) - return super(mrp_production,self).action_cancel(cr,uid,ids,context=context) - - def _compute_planned_workcenter(self, cr, uid, ids, context=None, mini=False): - """ Computes planned and finished dates for work order. - @return: Calculated date - """ - dt_end = datetime.now() - if context is None: - context = {} - for po in self.browse(cr, uid, ids, context=context): - dt_end = datetime.strptime(po.date_planned, '%Y-%m-%d %H:%M:%S') - if not po.date_start: - self.write(cr, uid, [po.id], { - 'date_start': po.date_planned - }, context=context, update=False) - old = None - for wci in range(len(po.workcenter_lines)): - wc = po.workcenter_lines[wci] - if (old is None) or (wc.sequence>old): - dt = dt_end - if context.get('__last_update'): - del context['__last_update'] - if (wc.date_planned < dt.strftime('%Y-%m-%d %H:%M:%S')) or mini: - self.pool.get('mrp.production.workcenter.line').write(cr, uid, [wc.id], { - 'date_planned': dt.strftime('%Y-%m-%d %H:%M:%S') - }, context=context, update=False) - i = self.pool.get('resource.calendar').interval_get( - cr, - uid, - wc.workcenter_id.calendar_id and wc.workcenter_id.calendar_id.id or False, - dt, - wc.hour or 0.0 - ) - if i: - dt_end = max(dt_end, i[-1][1]) - else: - dt_end = datetime.strptime(wc.date_planned_end, '%Y-%m-%d %H:%M:%S') - - old = wc.sequence or 0 - super(mrp_production, self).write(cr, uid, [po.id], { - 'date_finished': dt_end - }) - return dt_end - - def _move_pass(self, cr, uid, ids, context=None): - """ Calculates start date for stock moves finding interval from resource calendar. - @return: True - """ - for po in self.browse(cr, uid, ids, context=context): - if po.allow_reorder: - continue - todo = po.move_lines - dt = datetime.strptime(po.date_start,'%Y-%m-%d %H:%M:%S') - while todo: - l = todo.pop(0) - if l.state in ('done','cancel','draft'): - continue - todo += l.move_dest_id_lines - if l.production_id and (l.production_id.date_finished > dt.strftime('%Y-%m-%d %H:%M:%S')): - if l.production_id.state not in ('done','cancel'): - for wc in l.production_id.workcenter_lines: - i = self.pool.get('resource.calendar').interval_min_get( - cr, - uid, - wc.workcenter_id.calendar_id.id or False, - dt, wc.hour or 0.0 - ) - dt = i[0][0] - if l.production_id.date_start > dt.strftime('%Y-%m-%d %H:%M:%S'): - self.write(cr, uid, [l.production_id.id], {'date_start':dt.strftime('%Y-%m-%d %H:%M:%S')}, mini=True) - return True - - def _move_futur(self, cr, uid, ids, context=None): - """ Calculates start date for stock moves. - @return: True - """ - for po in self.browse(cr, uid, ids, context=context): - if po.allow_reorder: - continue - for line in po.move_created_ids: - l = line - while l.move_dest_id: - l = l.move_dest_id - if l.state in ('done','cancel','draft'): - break - if l.production_id.state in ('done','cancel'): - break - if l.production_id and (l.production_id.date_start < po.date_finished): - self.write(cr, uid, [l.production_id.id], {'date_start': po.date_finished}) - break - return True - - - def write(self, cr, uid, ids, vals, context=None, update=True, mini=True): - direction = {} - if vals.get('date_start', False): - for po in self.browse(cr, uid, ids, context=context): - direction[po.id] = cmp(po.date_start, vals.get('date_start', False)) - result = super(mrp_production, self).write(cr, uid, ids, vals, context=context) - if (vals.get('workcenter_lines', False) or vals.get('date_start', False) or vals.get('date_planned', False)) and update: - self._compute_planned_workcenter(cr, uid, ids, context=context, mini=mini) - for d in direction: - if direction[d] == 1: - # the production order has been moved to the passed - self._move_pass(cr, uid, [d], context=context) - pass - elif direction[d] == -1: - self._move_futur(cr, uid, [d], context=context) - # the production order has been moved to the future - pass - return result - - def action_compute(self, cr, uid, ids, properties=None, context=None): - """ Computes bills of material of a product and planned date of work order. - @param properties: List containing dictionaries of properties. - @return: No. of products. - """ - result = super(mrp_production, self).action_compute(cr, uid, ids, properties=properties, context=context) - self._compute_planned_workcenter(cr, uid, ids, context=context) - return result - -mrp_production() - -class mrp_operations_operation_code(osv.osv): - _name="mrp_operations.operation.code" - _columns={ - 'name': fields.char('Operation Name',size=64, required=True), - 'code': fields.char('Code', size=16, required=True), - 'start_stop': fields.selection([('start','Start'),('pause','Pause'),('resume','Resume'),('cancel','Cancelled'),('done','Done')], 'Status', required=True), - } -mrp_operations_operation_code() - -class mrp_operations_operation(osv.osv): - _name="mrp_operations.operation" - - def _order_date_search_production(self, cr, uid, ids, context=None): - """ Finds operations for a production order. - @return: List of ids - """ - operation_ids = self.pool.get('mrp_operations.operation').search(cr, uid, [('production_id','=',ids[0])], context=context) - return operation_ids - - def _get_order_date(self, cr, uid, ids, field_name, arg, context=None): - """ Calculates planned date for an operation. - @return: Dictionary of values - """ - res={} - operation_obj = self.browse(cr, uid, ids, context=context) - for operation in operation_obj: - res[operation.id] = operation.production_id.date_planned - return res - - def calc_delay(self, cr, uid, vals): - """ Calculates delay of work order. - @return: Delay - """ - code_lst = [] - time_lst = [] - - code_ids = self.pool.get('mrp_operations.operation.code').search(cr, uid, [('id','=',vals['code_id'])]) - code = self.pool.get('mrp_operations.operation.code').browse(cr, uid, code_ids)[0] - - oper_ids = self.search(cr,uid,[('production_id','=',vals['production_id']),('workcenter_id','=',vals['workcenter_id'])]) - oper_objs = self.browse(cr,uid,oper_ids) - - for oper in oper_objs: - code_lst.append(oper.code_id.start_stop) - time_lst.append(oper.date_start) - - code_lst.append(code.start_stop) - time_lst.append(vals['date_start']) - diff = 0 - for i in range(0,len(code_lst)): - if code_lst[i] == 'pause' or code_lst[i] == 'done' or code_lst[i] == 'cancel': - if not i: continue - if code_lst[i-1] not in ('resume','start'): - continue - a = datetime.strptime(time_lst[i-1],'%Y-%m-%d %H:%M:%S') - b = datetime.strptime(time_lst[i],'%Y-%m-%d %H:%M:%S') - diff += (b-a).days * 24 - diff += (b-a).seconds / float(60*60) - return diff - - def check_operation(self, cr, uid, vals): - """ Finds which operation is called ie. start, pause, done, cancel. - @param vals: Dictionary of values. - @return: True or False - """ - code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])]) - code=self.pool.get('mrp_operations.operation.code').browse(cr,uid,code_ids)[0] - code_lst = [] - oper_ids=self.search(cr,uid,[('production_id','=',vals['production_id']),('workcenter_id','=',vals['workcenter_id'])]) - oper_objs=self.browse(cr,uid,oper_ids) - - if not oper_objs: - if code.start_stop!='start': - raise osv.except_osv(_('Sorry!'),_('Operation is not started yet!')) - return False - else: - for oper in oper_objs: - code_lst.append(oper.code_id.start_stop) - if code.start_stop=='start': - if 'start' in code_lst: - raise osv.except_osv(_('Sorry!'),_('Operation has already started! You can either Pause/Finish/Cancel the operation.')) - return False - if code.start_stop=='pause': - if code_lst[len(code_lst)-1]!='resume' and code_lst[len(code_lst)-1]!='start': - raise osv.except_osv(_('Error!'),_('In order to Pause the operation, it must be in the Start or Resume state!')) - return False - if code.start_stop=='resume': - if code_lst[len(code_lst)-1]!='pause': - raise osv.except_osv(_('Error!'),_('In order to Resume the operation, it must be in the Pause state!')) - return False - - if code.start_stop=='done': - if code_lst[len(code_lst)-1]!='start' and code_lst[len(code_lst)-1]!='resume': - raise osv.except_osv(_('Sorry!'),_('In order to Finish the operation, it must be in the Start or Resume state!')) - return False - if 'cancel' in code_lst: - raise osv.except_osv(_('Sorry!'),_('Operation is Already Cancelled!')) - return False - if code.start_stop=='cancel': - if not 'start' in code_lst : - raise osv.except_osv(_('Error!'),_('No operation to cancel.')) - return False - if 'done' in code_lst: - raise osv.except_osv(_('Error!'),_('Operation is already finished!')) - return False - return True - - def write(self, cr, uid, ids, vals, context=None): - oper_objs = self.browse(cr, uid, ids, context=context)[0] - vals['production_id']=oper_objs.production_id.id - vals['workcenter_id']=oper_objs.workcenter_id.id - - if 'code_id' in vals: - self.check_operation(cr, uid, vals) - - if 'date_start' in vals: - vals['date_start']=vals['date_start'] - vals['code_id']=oper_objs.code_id.id - delay=self.calc_delay(cr, uid, vals) - wc_op_id=self.pool.get('mrp.production.workcenter.line').search(cr,uid,[('workcenter_id','=',vals['workcenter_id']),('production_id','=',vals['production_id'])]) - self.pool.get('mrp.production.workcenter.line').write(cr,uid,wc_op_id,{'delay':delay}) - - return super(mrp_operations_operation, self).write(cr, uid, ids, vals, context=context) - - def create(self, cr, uid, vals, context=None): - wf_service = netsvc.LocalService('workflow') - code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])]) - code=self.pool.get('mrp_operations.operation.code').browse(cr, uid, code_ids, context=context)[0] - wc_op_id=self.pool.get('mrp.production.workcenter.line').search(cr,uid,[('workcenter_id','=',vals['workcenter_id']),('production_id','=',vals['production_id'])]) - if code.start_stop in ('start','done','pause','cancel','resume'): - if not wc_op_id: - production_obj=self.pool.get('mrp.production').browse(cr, uid, vals['production_id'], context=context) - wc_op_id.append(self.pool.get('mrp.production.workcenter.line').create(cr,uid,{'production_id':vals['production_id'],'name':production_obj.product_id.name,'workcenter_id':vals['workcenter_id']})) - if code.start_stop=='start': - self.pool.get('mrp.production.workcenter.line').action_start_working(cr,uid,wc_op_id) - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_start_working', cr) - - - if code.start_stop=='done': - self.pool.get('mrp.production.workcenter.line').action_done(cr,uid,wc_op_id) - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_done', cr) - self.pool.get('mrp.production').write(cr,uid,vals['production_id'],{'date_finished':datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) - - if code.start_stop=='pause': - self.pool.get('mrp.production.workcenter.line').action_pause(cr,uid,wc_op_id) - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_pause', cr) - - if code.start_stop=='resume': - self.pool.get('mrp.production.workcenter.line').action_resume(cr,uid,wc_op_id) - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_resume', cr) - - if code.start_stop=='cancel': - self.pool.get('mrp.production.workcenter.line').action_cancel(cr,uid,wc_op_id) - wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_cancel', cr) - - if not self.check_operation(cr, uid, vals): - return - delay=self.calc_delay(cr, uid, vals) - line_vals = {} - line_vals['delay'] = delay - if vals.get('date_start',False): - if code.start_stop == 'done': - line_vals['date_finished'] = vals['date_start'] - elif code.start_stop == 'start': - line_vals['date_start'] = vals['date_start'] - - self.pool.get('mrp.production.workcenter.line').write(cr, uid, wc_op_id, line_vals, context=context) - - return super(mrp_operations_operation, self).create(cr, uid, vals, context=context) - - def initialize_workflow_instance(self, cr, uid, context=None): - wf_service = netsvc.LocalService("workflow") - line_ids = self.pool.get('mrp.production.workcenter.line').search(cr, uid, [], context=context) - for line_id in line_ids: - wf_service.trg_create(uid, 'mrp.production.workcenter.line', line_id, cr) - return True - - _columns={ - 'production_id':fields.many2one('mrp.production','Production',required=True), - 'workcenter_id':fields.many2one('mrp.workcenter','Work Center',required=True), - 'code_id':fields.many2one('mrp_operations.operation.code','Code',required=True), - 'date_start': fields.datetime('Start Date'), - 'date_finished': fields.datetime('End Date'), - 'order_date': fields.function(_get_order_date,string='Order Date',type='date',store={'mrp.production':(_order_date_search_production,['date_planned'], 10)}), - } - _defaults={ - 'date_start': lambda *a:datetime.now().strftime('%Y-%m-%d %H:%M:%S') - } - -mrp_operations_operation() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - +# -*- coding: utf-8 -*- +############################################################################## +# +# VNC Developments (India) Pvt. Ltd. +# Copyright (C) 2004-TODAY VNC (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.osv import fields +from openerp.osv import osv +from openerp import netsvc +import time +from datetime import datetime +from openerp.tools.translate import _ + +class stock_move(osv.osv): + _inherit = 'stock.move' + _columns = {'move_dest_id_lines': fields.one2many('stock.move', 'move_dest_id', 'Children Moves')} + + def copy(self, cr, uid, id, default = None, context = None): + if default is None: + default = {} + default.update({'move_dest_id_lines': []}) + return super(stock_move, self).copy(cr, uid, id, default, context) + + +stock_move() + +class mrp_production_workcenter_line(osv.osv): + + def _get_date_end(self, cr, uid, ids, field_name, arg, context = None): + """ Finds ending date. + @return: Dictionary of values. + """ + ops = self.browse(cr, uid, ids, context=context) + date_and_hours_by_cal = [ (op.date_planned, op.hour, op.workcenter_id.calendar_id.id) for op in ops if op.date_planned ] + intervals = self.pool.get('resource.calendar').interval_get_multi(cr, uid, date_and_hours_by_cal) + res = {} + for op in ops: + res[op.id] = False + if op.date_planned: + i = intervals.get((op.date_planned, op.hour, op.workcenter_id.calendar_id.id)) + if i: + res[op.id] = i[-1][1].strftime('%Y-%m-%d %H:%M:%S') + else: + res[op.id] = op.date_planned + + return res + + def onchange_production_id(self, cr, uid, ids, production_id, context = None): + if not production_id: + return {} + else: + production = self.pool.get('mrp.production').browse(cr, uid, production_id, context=None) + result = {'product': production.product_id.id, + 'qty': production.product_qty, + 'uom': production.product_uom.id} + return {'value': result} + + _inherit = 'mrp.production.workcenter.line' + _order = 'sequence, date_planned' + _columns = {'state': fields.selection([('draft', 'Draft'), + ('cancel', 'Cancelled'), + ('pause', 'Pending'), + ('startworking', 'In Progress'), + ('done', 'Finished')], 'Status', readonly=True, help="* When a work order is created it is set in 'Draft' status.\n* When user sets work order in start mode that time it will be set in 'In Progress' status.\n* When work order is in running mode, during that time if user wants to stop or to make changes in order then can set in 'Pending' status.\n* When the user cancels the work order it will be set in 'Canceled' status.\n* When order is completely processed that time it is set in 'Finished' status."), + 'date_planned': fields.datetime('Scheduled Date', select=True), + 'date_planned_end': fields.function(_get_date_end, string='End Date', type='datetime'), + 'date_start': fields.datetime('Start Date'), + 'date_finished': fields.datetime('End Date'), + 'delay': fields.float('Working Hours', help='The elapsed time between operation start and stop in this Work Center', readonly=True), + 'production_state': fields.related('production_id', 'state', type='selection', selection=[('draft', 'Draft'), + ('picking_except', 'Picking Exception'), + ('confirmed', 'Waiting Goods'), + ('ready', 'Ready to Produce'), + ('in_production', 'In Production'), + ('cancel', 'Canceled'), + ('done', 'Done')], string='Production Status', readonly=True), + 'product': fields.related('production_id', 'product_id', type='many2one', relation='product.product', string='Product', readonly=True), + 'qty': fields.related('production_id', 'product_qty', type='float', string='Qty', readonly=True, store=True), + 'uom': fields.related('production_id', 'product_uom', type='many2one', relation='product.uom', string='Unit of Measure', readonly=True)} + _defaults = {'state': 'draft', + 'delay': 0.0, + 'production_state': 'draft'} + + def modify_production_order_state(self, cr, uid, ids, action): + """ Modifies production order state if work order state is changed. + @param action: Action to perform. + @return: Nothing + """ + wf_service = netsvc.LocalService('workflow') + prod_obj_pool = self.pool.get('mrp.production') + oper_obj = self.browse(cr, uid, ids)[0] + prod_obj = oper_obj.production_id + if action == 'start': + if prod_obj.state == 'confirmed': + prod_obj_pool.force_production(cr, uid, [prod_obj.id]) + wf_service.trg_validate(uid, 'mrp.production', prod_obj.id, 'button_produce', cr) + elif prod_obj.state == 'ready': + wf_service.trg_validate(uid, 'mrp.production', prod_obj.id, 'button_produce', cr) + else: + if prod_obj.state == 'in_production': + return + raise osv.except_osv(_('Error!'), _('Manufacturing order cannot be started in state "%s"!') % (prod_obj.state,)) + else: + oper_ids = self.search(cr, uid, [('production_id', '=', prod_obj.id)]) + obj = self.browse(cr, uid, oper_ids) + flag = True + for line in obj: + if line.state != 'done': + flag = False + + if flag: + for production in prod_obj_pool.browse(cr, uid, [prod_obj.id], context=None): + if production.move_lines or production.move_created_ids: + prod_obj_pool.action_produce(cr, uid, production.id, production.product_qty, 'consume_produce', context=None) + + wf_service.trg_validate(uid, 'mrp.production', oper_obj.production_id.id, 'button_produce_done', cr) + return + + def write(self, cr, uid, ids, vals, context = None, update = True): + result = super(mrp_production_workcenter_line, self).write(cr, uid, ids, vals, context=context) + prod_obj = self.pool.get('mrp.production') + if vals.get('date_planned', False) and update: + for prod in self.browse(cr, uid, ids, context=context): + if prod.production_id.workcenter_lines: + dstart = min(vals['date_planned'], prod.production_id.workcenter_lines[0]['date_planned']) + prod_obj.write(cr, uid, [prod.production_id.id], {'date_start': dstart}, context=context, mini=False) + + return result + + def action_draft(self, cr, uid, ids, context = None): + """ Sets state to draft. + @return: True + """ + return self.write(cr, uid, ids, {'state': 'draft'}, context=context) + + def action_start_working(self, cr, uid, ids, context = None): + """ Sets state to start working and writes starting date. + @return: True + """ + self.modify_production_order_state(cr, uid, ids, 'start') + self.write(cr, uid, ids, {'state': 'startworking', + 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context) + return True + + def action_done(self, cr, uid, ids, context = None): + """ Sets state to done, writes finish date and calculates delay. + @return: True + """ + delay = 0.0 + date_now = time.strftime('%Y-%m-%d %H:%M:%S') + obj_line = self.browse(cr, uid, ids[0]) + date_start = datetime.strptime(obj_line.date_start, '%Y-%m-%d %H:%M:%S') + date_finished = datetime.strptime(date_now, '%Y-%m-%d %H:%M:%S') + delay += (date_finished - date_start).days * 24 + delay += (date_finished - date_start).seconds / float(3600) + self.write(cr, uid, ids, {'state': 'done', + 'date_finished': date_now, + 'delay': delay}, context=context) + self.modify_production_order_state(cr, uid, ids, 'done') + return True + + def action_cancel(self, cr, uid, ids, context = None): + """ Sets state to cancel. + @return: True + """ + return self.write(cr, uid, ids, {'state': 'cancel'}, context=context) + + def action_pause(self, cr, uid, ids, context = None): + """ Sets state to pause. + @return: True + """ + return self.write(cr, uid, ids, {'state': 'pause'}, context=context) + + def action_resume(self, cr, uid, ids, context = None): + """ Sets state to startworking. + @return: True + """ + return self.write(cr, uid, ids, {'state': 'startworking'}, context=context) + + +class mrp_production(osv.osv): + _inherit = 'mrp.production' + _columns = {'allow_reorder': fields.boolean('Free Serialisation', help='Check this to be able to move independently all production orders, without moving dependent ones.')} + + def _production_date_end(self, cr, uid, ids, prop, unknow_none, context = None): + """ Calculates planned end date of production order. + @return: Dictionary of values + """ + result = {} + for prod in self.browse(cr, uid, ids, context=context): + result[prod.id] = prod.date_planned + for line in prod.workcenter_lines: + result[prod.id] = max(line.date_planned_end, result[prod.id]) + + return result + + def action_production_end(self, cr, uid, ids): + """ Finishes work order if production order is done. + @return: Super method + """ + obj = self.browse(cr, uid, ids)[0] + wf_service = netsvc.LocalService('workflow') + for workcenter_line in obj.workcenter_lines: + if workcenter_line.state == 'draft': + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_start_working', cr) + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_done', cr) + + return super(mrp_production, self).action_production_end(cr, uid, ids) + + def action_in_production(self, cr, uid, ids): + """ Changes state to In Production and writes starting date. + @return: True + """ + obj = self.browse(cr, uid, ids)[0] + workcenter_pool = self.pool.get('mrp.production.workcenter.line') + wf_service = netsvc.LocalService('workflow') + for prod in self.browse(cr, uid, ids): + if prod.workcenter_lines: + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', prod.workcenter_lines[0].id, 'button_start_working', cr) + + return super(mrp_production, self).action_in_production(cr, uid, ids) + + def action_cancel(self, cr, uid, ids, context = None): + """ Cancels work order if production order is canceled. + @return: Super method + """ + obj = self.browse(cr, uid, ids, context=context)[0] + wf_service = netsvc.LocalService('workflow') + for workcenter_line in obj.workcenter_lines: + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_cancel', cr) + + return super(mrp_production, self).action_cancel(cr, uid, ids, context=context) + + def _compute_planned_workcenter(self, cr, uid, ids, context = None, mini = False): + """ Computes planned and finished dates for work order. + @return: Calculated date + """ + dt_end = datetime.now() + if context is None: + context = {} + for po in self.browse(cr, uid, ids, context=context): + dt_end = datetime.strptime(po.date_planned, '%Y-%m-%d %H:%M:%S') + if not po.date_start: + self.write(cr, uid, [po.id], {'date_start': po.date_planned}, context=context, update=False) + old = None + for wci in range(len(po.workcenter_lines)): + wc = po.workcenter_lines[wci] + if old is None or wc.sequence > old: + dt = dt_end + if context.get('__last_update'): + del context['__last_update'] + if wc.date_planned < dt.strftime('%Y-%m-%d %H:%M:%S') or mini: + self.pool.get('mrp.production.workcenter.line').write(cr, uid, [wc.id], {'date_planned': dt.strftime('%Y-%m-%d %H:%M:%S')}, context=context, update=False) + i = self.pool.get('resource.calendar').interval_get(cr, uid, wc.workcenter_id.calendar_id and wc.workcenter_id.calendar_id.id or False, dt, wc.hour or 0.0) + if i: + dt_end = max(dt_end, i[-1][1]) + else: + dt_end = datetime.strptime(wc.date_planned_end, '%Y-%m-%d %H:%M:%S') + old = wc.sequence or 0 + + super(mrp_production, self).write(cr, uid, [po.id], {'date_finished': dt_end}) + + return dt_end + + def _move_pass(self, cr, uid, ids, context = None): + """ Calculates start date for stock moves finding interval from resource calendar. + @return: True + """ + for po in self.browse(cr, uid, ids, context=context): + if po.allow_reorder: + continue + todo = po.move_lines + dt = datetime.strptime(po.date_start, '%Y-%m-%d %H:%M:%S') + while todo: + l = todo.pop(0) + if l.state in ('done', 'cancel', 'draft'): + continue + todo += l.move_dest_id_lines + if l.production_id and l.production_id.date_finished > dt: + if l.production_id.state not in ('done', 'cancel'): + for wc in l.production_id.workcenter_lines: + i = self.pool.get('resource.calendar').interval_min_get(cr, uid, wc.workcenter_id.calendar_id.id or False, dt, wc.hour or 0.0) + dt = i[0][0] + + if l.production_id.date_start > dt.strftime('%Y-%m-%d %H:%M:%S'): + self.write(cr, uid, [l.production_id.id], {'date_start': dt.strftime('%Y-%m-%d %H:%M:%S')}, mini=True) + + return True + + def _move_futur(self, cr, uid, ids, context = None): + """ Calculates start date for stock moves. + @return: True + """ + for po in self.browse(cr, uid, ids, context=context): + if po.allow_reorder: + continue + for line in po.move_created_ids: + l = line + while l.move_dest_id: + l = l.move_dest_id + if l.state in ('done', 'cancel', 'draft'): + break + if l.production_id.state in ('done', 'cancel'): + break + if l.production_id and l.production_id.date_start < po.date_finished: + self.write(cr, uid, [l.production_id.id], {'date_start': po.date_finished}) + break + + return True + + def write(self, cr, uid, ids, vals, context = None, update = True, mini = True): + direction = {} + if vals.get('date_start', False): + for po in self.browse(cr, uid, ids, context=context): + direction[po.id] = cmp(po.date_start, vals.get('date_start', False)) + + result = super(mrp_production, self).write(cr, uid, ids, vals, context=context) + if (vals.get('workcenter_lines', False) or vals.get('date_start', False)) and update: + self._compute_planned_workcenter(cr, uid, ids, context=context, mini=mini) + for d in direction: + if direction[d] == 1: + self._move_pass(cr, uid, [d], context=context) + continue + direction[d] == -1 and self._move_futur(cr, uid, [d], context=context) + + return result + + def action_compute(self, cr, uid, ids, properties = None, context = None): + """ Computes bills of material of a product and planned date of work order. + @param properties: List containing dictionaries of properties. + @return: No. of products. + """ + result = super(mrp_production, self).action_compute(cr, uid, ids, properties=properties, context=context) + self._compute_planned_workcenter(cr, uid, ids, context=context) + return result + + +mrp_production() + +class mrp_operations_operation_code(osv.osv): + _name = 'mrp_operations.operation.code' + _columns = {'name': fields.char('Operation Name', size=64, required=True), + 'code': fields.char('Code', size=16, required=True), + 'start_stop': fields.selection([('start', 'Start'), + ('pause', 'Pause'), + ('resume', 'Resume'), + ('cancel', 'Cancelled'), + ('done', 'Done')], 'Status', required=True)} + + +mrp_operations_operation_code() + +class mrp_operations_operation(osv.osv): + _name = 'mrp_operations.operation' + + def _order_date_search_production(self, cr, uid, ids, context = None): + """ Finds operations for a production order. + @return: List of ids + """ + operation_ids = self.pool.get('mrp_operations.operation').search(cr, uid, [('production_id', '=', ids[0])], context=context) + return operation_ids + + def _get_order_date(self, cr, uid, ids, field_name, arg, context = None): + """ Calculates planned date for an operation. + @return: Dictionary of values + """ + res = {} + operation_obj = self.browse(cr, uid, ids, context=context) + for operation in operation_obj: + res[operation.id] = operation.production_id.date_planned + + return res + + def calc_delay(self, cr, uid, vals): + """ Calculates delay of work order. + @return: Delay + """ + code_lst = [] + time_lst = [] + code_ids = self.pool.get('mrp_operations.operation.code').search(cr, uid, [('id', '=', vals['code_id'])]) + code = self.pool.get('mrp_operations.operation.code').browse(cr, uid, code_ids)[0] + oper_ids = self.search(cr, uid, [('production_id', '=', vals['production_id']), ('workcenter_id', '=', vals['workcenter_id'])]) + oper_objs = self.browse(cr, uid, oper_ids) + for oper in oper_objs: + code_lst.append(oper.code_id.start_stop) + time_lst.append(oper.date_start) + + code_lst.append(code.start_stop) + time_lst.append(vals['date_start']) + diff = 0 + for i in range(0, len(code_lst)): + if code_lst[i] == 'pause' or code_lst[i] == 'done' or code_lst[i] == 'cancel': + if not i: + continue + if code_lst[i - 1] not in ('resume', 'start'): + continue + a = datetime.strptime(time_lst[i - 1], '%Y-%m-%d %H:%M:%S') + b = datetime.strptime(time_lst[i], '%Y-%m-%d %H:%M:%S') + diff += (b - a).days * 24 + diff += (b - a).seconds / float(3600) + + return diff + + def check_operation(self, cr, uid, vals): + """ Finds which operation is called ie. start, pause, done, cancel. + @param vals: Dictionary of values. + @return: True or False + """ + code_ids = self.pool.get('mrp_operations.operation.code').search(cr, uid, [('id', '=', vals['code_id'])]) + code = self.pool.get('mrp_operations.operation.code').browse(cr, uid, code_ids)[0] + code_lst = [] + oper_ids = self.search(cr, uid, [('production_id', '=', vals['production_id']), ('workcenter_id', '=', vals['workcenter_id'])]) + oper_objs = self.browse(cr, uid, oper_ids) + if not oper_objs: + if code.start_stop != 'start': + raise osv.except_osv(_('Sorry!'), _('Operation is not started yet!')) + return False + else: + for oper in oper_objs: + code_lst.append(oper.code_id.start_stop) + + if code.start_stop == 'start': + if 'start' in code_lst: + raise osv.except_osv(_('Sorry!'), _('Operation has already started! You can either Pause/Finish/Cancel the operation.')) + return False + if code.start_stop == 'pause': + if code_lst[len(code_lst) - 1] != 'resume' and code_lst[len(code_lst) - 1] != 'start': + raise osv.except_osv(_('Error!'), _('In order to Pause the operation, it must be in the Start or Resume state!')) + return False + if code.start_stop == 'resume': + if code_lst[len(code_lst) - 1] != 'pause': + raise osv.except_osv(_('Error!'), _('In order to Resume the operation, it must be in the Pause state!')) + return False + if code.start_stop == 'done': + if code_lst[len(code_lst) - 1] != 'start' and code_lst[len(code_lst) - 1] != 'resume': + raise osv.except_osv(_('Sorry!'), _('In order to Finish the operation, it must be in the Start or Resume state!')) + return False + if 'cancel' in code_lst: + raise osv.except_osv(_('Sorry!'), _('Operation is Already Cancelled!')) + return False + if code.start_stop == 'cancel': + if 'start' not in code_lst: + raise osv.except_osv(_('Error!'), _('No operation to cancel.')) + return False + if 'done' in code_lst: + raise osv.except_osv(_('Error!'), _('Operation is already finished!')) + return False + return True + + def write(self, cr, uid, ids, vals, context = None): + oper_objs = self.browse(cr, uid, ids, context=context)[0] + vals['production_id'] = oper_objs.production_id.id + vals['workcenter_id'] = oper_objs.workcenter_id.id + if 'code_id' in vals: + self.check_operation(cr, uid, vals) + if 'date_start' in vals: + vals['date_start'] = vals['date_start'] + vals['code_id'] = oper_objs.code_id.id + delay = self.calc_delay(cr, uid, vals) + wc_op_id = self.pool.get('mrp.production.workcenter.line').search(cr, uid, [('workcenter_id', '=', vals['workcenter_id']), ('production_id', '=', vals['production_id'])]) + self.pool.get('mrp.production.workcenter.line').write(cr, uid, wc_op_id, {'delay': delay}) + return super(mrp_operations_operation, self).write(cr, uid, ids, vals, context=context) + + def create(self, cr, uid, vals, context = None): + wf_service = netsvc.LocalService('workflow') + code_ids = self.pool.get('mrp_operations.operation.code').search(cr, uid, [('id', '=', vals['code_id'])]) + code = self.pool.get('mrp_operations.operation.code').browse(cr, uid, code_ids, context=context)[0] + wc_op_id = self.pool.get('mrp.production.workcenter.line').search(cr, uid, [('workcenter_id', '=', vals['workcenter_id']), ('production_id', '=', vals['production_id'])]) + if code.start_stop in ('start', 'done', 'pause', 'cancel', 'resume'): + if not wc_op_id: + production_obj = self.pool.get('mrp.production').browse(cr, uid, vals['production_id'], context=context) + wc_op_id.append(self.pool.get('mrp.production.workcenter.line').create(cr, uid, {'production_id': vals['production_id'], + 'name': production_obj.product_id.name, + 'workcenter_id': vals['workcenter_id']})) + if code.start_stop == 'start': + self.pool.get('mrp.production.workcenter.line').action_start_working(cr, uid, wc_op_id) + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_start_working', cr) + if code.start_stop == 'done': + self.pool.get('mrp.production.workcenter.line').action_done(cr, uid, wc_op_id) + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_done', cr) + self.pool.get('mrp.production').write(cr, uid, vals['production_id'], {'date_finished': datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) + if code.start_stop == 'pause': + self.pool.get('mrp.production.workcenter.line').action_pause(cr, uid, wc_op_id) + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_pause', cr) + if code.start_stop == 'resume': + self.pool.get('mrp.production.workcenter.line').action_resume(cr, uid, wc_op_id) + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_resume', cr) + if code.start_stop == 'cancel': + self.pool.get('mrp.production.workcenter.line').action_cancel(cr, uid, wc_op_id) + wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_cancel', cr) + if not self.check_operation(cr, uid, vals): + return + delay = self.calc_delay(cr, uid, vals) + line_vals = {} + line_vals['delay'] = delay + if vals.get('date_start', False): + if code.start_stop == 'done': + line_vals['date_finished'] = vals['date_start'] + elif code.start_stop == 'start': + line_vals['date_start'] = vals['date_start'] + self.pool.get('mrp.production.workcenter.line').write(cr, uid, wc_op_id, line_vals, context=context) + return super(mrp_operations_operation, self).create(cr, uid, vals, context=context) + + def initialize_workflow_instance(self, cr, uid, context = None): + wf_service = netsvc.LocalService('workflow') + line_ids = self.pool.get('mrp.production.workcenter.line').search(cr, uid, [], context=context) + for line_id in line_ids: + wf_service.trg_create(uid, 'mrp.production.workcenter.line', line_id, cr) + + return True + + _columns = {'production_id': fields.many2one('mrp.production', 'Production', required=True), + 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), + 'code_id': fields.many2one('mrp_operations.operation.code', 'Code', required=True), + 'date_start': fields.datetime('Start Date'), + 'date_finished': fields.datetime('End Date'), + 'order_date': fields.function(_get_order_date, string='Order Date', type='date', store={'mrp.production': (_order_date_search_production, ['date_planned'], 10)})} + _defaults = {'date_start': lambda *a: datetime.now().strftime('%Y-%m-%d %H:%M:%S')} + + +mrp_operations_operation() \ No newline at end of file diff --git a/addons/mrp_operations/mrp_operations_view.xml b/addons/mrp_operations/mrp_operations_view.xml index e85896e94a59b..207c5d975d783 100644 --- a/addons/mrp_operations/mrp_operations_view.xml +++ b/addons/mrp_operations/mrp_operations_view.xml @@ -9,7 +9,7 @@
    -
    -