一、创作初衷
作者需要找一份比较好的销售合同台账管理模型,但找到的资料要么要么不全,要么不能指导实际工作,基本不尽人意。鉴于此,考虑自己搭建一份销售合同台账管理模型。
基本思路是:1.通过python生成模拟数据,模拟数据包括客户信息表、销售合同台账、销售发票台账和销售回款明细表,通过严谨的关联性设计和数值计算逻辑,确保模拟数据符合真实业务场景需求。2.对生成的模拟数据,运用power query进行数据处理,做到后期更新维护简单及时准确。3.运用power bi对整理后的数据进行可视化展示,主要展示点在基础指标分析、分合同分客户分析、分时期分析、异常情况检测等;4.该销售合同台账模型在实际工作中如何运用,各基础表的更新工作由哪些业务部门或业务人员完成,主要在指导实际工作方面。
本文主要展示如何使用python生成模拟数据。在生成模拟数据时,主要考虑以下逻辑:1.客户信息表中,客户名称唯一、客户编号唯一;2.销售合同台账中,合同编号唯一,且根据时间先后递增;客户名称取自客户明细表;3.销售发票台账中,客户名称取自客户信息表、合同编号取自销售合同台账;为贴近业务实际,单笔发票可开具多笔合同,但为便于模型分析,需对发票金额按照所对应合同金额拆分处理;销售发票开票时间不得早于合同签订日期。4.销售回款表中,客户名称取自客户信息表、合同编号取自销售合同台账;为贴近业务实际,单笔收款单可收取多笔合同货款,但为便于模型分析,需对收款单金额按照所对应合同金额拆分处理。5.单笔合同开具的发票合计金额及收取的货款合计金额不得突破合同总额。
以下是详细内容。
1. 数据建模架构设计
模拟的销售业务系统包含4个核心模块:客户信息表、销售合同台账、销售发票台账、应收账款回款明细表。
2. Python实现详解
2.1 环境配置
# 基础库
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# 数据模拟
from faker import Faker
from decimal import Decimal, getcontext
# 初始化设置
fake = Faker("zh_CN") # 中文数据生成器
getcontext().rounding = ROUND_HALF_UP # 财务精确计算模式
2.2 客户信息生成
业务逻辑:
- 生成300家虚拟客户
- 确保客户名称不重复
- 包含统一社会信用代码等合规信息
核心代码亮点:
def generate_customers(num=300):
used_names = set()
# 生成符合规范的18位信用代码
def generate_credit_code():
return '91' + fake.bothify('###############').upper() # 91开头+15位随机码
while len(customers) < num:
name = fake.company() # 生成中文公司名
if name not in used_names:
customers.append({
"客户编号": f"{customer_counter:08d}", # 8位数字编号
"统一社会信用代码": generate_credit_code(),
"所在省份": fake.province() # 省份信息
})
return pd.DataFrame(customers)
2.3 销售合同台账生成
关键技术点:
- 时间序列控制:确保同一年份合同按签订时间顺序编号
- 金额精度处理:使用Decimal类型避免浮点误差
- 唯一性保证:通过微秒级时间戳避免重复日期
def generate_contracts(customers_df, num=500): for idx in range(num): # 生成带微秒的签订日期(关键修正点) sign_date = fake.date_time_between_dates(start_date, end_date).replace( microsecond=idx % 1000 ) # 年度连续编号生成(修正后版本) df['合同编号'] = df.groupby('year').cumcount() + 1 df['合同编号'] = df.apply( lambda row: f"{row['year']}-{row['合同编号']:08d}", axis=1 ) # 到期日期生成(90-210天随机间隔) days = np.random.choice(range(90, 211, 10), size=len(df)) df['合同到期日期'] = df['合同签订日期'] + pd.to_timedelta(days, unit='D')
2.4 发票与应收账款生成
业务规则实现:
- 发票拆分:单个发票可关联多个合同
- 税金计算:13%增值税精确拆分
- 收款关联:基于发票日期生成收款记录
# 增值税拆分示例 tax_rate = Decimal('0.13') tax_excluded = (alloc_amount / (1 + tax_rate)).quantize(Decimal('0.00')) tax = alloc_amount - tax_excluded # 收款日期生成逻辑 receipt_date = invoice['开具日期'] + timedelta( days=random.randint(10, 30) # 开票后10-30天收款 )
3. 数据验证方法
3.1 完整性检查
print(df_contracts.info()) # 输出示例 <class 'pandas.core.frame.DataFrame'> RangeIndex: 500 entries, 0 to 499 Data columns (total 5 columns): 合同编号 500 non-null object 合同签订日期 500 non-null datetime64[ns] 合同总额 500 non-null decimal.Decimal ...
3.2 业务规则验证
# 验证合同编号顺序性 def validate_contracts(df): # 拆解年份和序号 df_verify = df.copy() df_verify[['year_part', 'seq_part']] = df_verify['合同编号'].str.split('-', expand=True) # 检查日期单调性 assert df_verify.groupby('year_part')['合同签订日期'].is_monotonic_increasing.all()
4. 生成数据示例
客户信息表片段
客户编号 客户名称 统一社会信用代码 00000001 华腾科技有限公司 91310112MA7JZ3YQ1R 合同台账片段
合同编号 签订日期 合同总额 到期日期 2021-00000001 2021-03-12 1,200,000 2021-09-20 5. 常见问题排查
Q1:合同日期跨年顺序错误
现象:验证时抛出
跨年顺序错误
解决方案: 检查date_time_between_dates
的年份范围设置,确保不会生成跨年数据Q2:发票金额超过合同总额
调试方法:
# 在发票生成循环中添加检查 assert alloc_amount <= remaining_dict[c['合同编号']], \ f"金额超限: 合同{c['合同编号']} 剩余{remaining_dict[c['合同编号']]} 分配{alloc_amount}"
下一篇预告
《销售合同台账模型实战(二):Power BI可视化建模》 将涵盖:
- 数据关系模型搭建
- 应收账款账龄分析
- 客户信用风险看板
- 自动化更新机制