Beispiel #1
0
class OutputForm(FlaskForm):
    output_report = SelectField('出力帳票選択', [DataRequired()],
                                choices=OutputType.get_type_for_select(),
                                render_kw={"title": "出力帳票選択"})
    month = BeginningOfMonthField('年月', [DataRequired()],
                                  format='%Y/%m',
                                  render_kw={"autocomplete": "off"})
Beispiel #2
0
class BankForm(FlaskForm):
    id = IntegerField('Id')
    bank_name = StringField('銀行名称(必須)', [DataRequired(), Length(max=32)])
    text_for_document = StringField(
        '書類提出用文言名称(必須)', [DataRequired(), Length(max=128)])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')
Beispiel #3
0
class EngineerForm(FlaskForm):
    id = IntegerField('Id')
    engineer_name = StringField('技術者名称(必須)', [Length(max=128), DataRequired()])
    engineer_name_kana = StringField('技術者名称カナ', [Length(max=128)],
                                     filters=[lambda x: x or None])
    birthday = DateField('生年月日', [validators.optional()],
                         format='%Y/%m/%d',
                         render_kw={"autocomplete": "off"})
    gender = RadioField('性別', choices=Gender.get_gender_for_radio())
    company_id = SelectFieldWithSubtext('所属会社(必須)', [DataRequired()],
                                        render_kw={
                                            "title": "所属会社(必須)",
                                            "data-size": "8",
                                            "data-live-search": "true"
                                        })
    business_category = SelectMultipleField('業種', [Length(max=2048)],
                                            coerce=int,
                                            render_kw={
                                                "title": "業種(複数選択)",
                                                "data-size": "8",
                                                "data-live-search": "true",
                                                "data-actions-box": "true"
                                            })
    skill = SelectMultipleField('スキル', [Length(max=2048)],
                                coerce=int,
                                render_kw={
                                    "title": "スキル(複数選択)",
                                    "data-size": "8",
                                    "data-live-search": "true",
                                    "data-actions-box": "true"
                                })
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')
class LoginForm(FlaskForm):
    shain_number = StringField('社員番号', [DataRequired()])
    password = PasswordField('パスワード', [DataRequired()])

    def validate_password(self, field):
        user = repository.find_by_shain_number(self.shain_number.data)
        if user is None or user.can_not_login(field.data):
            raise ValidationError('社員番号またはパスワードが違います。')
Beispiel #5
0
class UserForm(FlaskForm):
    id = IntegerField('Id')
    shain_number = StringField('社員番号(必須)', [DataRequired(), Length(max=32)])
    user_name = StringField('ユーザー名称(必須)', [DataRequired(), Length(max=128)])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_shain_number(self, field):
        user = repository.find_by_shain_number(shain_number=field.data)
        if user and user.id != self.id.data:
            raise ValidationError('この社員番号は既に登録されています。')
Beispiel #6
0
class PwChangeForm(FlaskForm):
    password = PasswordField('現在のパスワード', [DataRequired()])
    new_password = PasswordField('新しいパスワード',[DataRequired()])
    new_password_confirmation = PasswordField('新しいパスワード(確認)',
                                              [DataRequired(),
                                               EqualTo('new_password',
                                                       message='新しいパスワードと新しいパスワード(確認)が一致していません')])

    def validate_password(self, field):
        user = repository.find_by_id(session['user']['id'])
        if user and not user.can_login(field.data):
            raise ValidationError('現在のパスワードが違います。')
class ProjectCreateForm(FlaskForm):
    project_name = StringField(
        'プロジェクト名称(必須)', [DataRequired(), Length(max=128)])
    project_name_for_bp = StringField('BP向けプロジェクト名称', [Length(max=128)],
                                      filters=[lambda x: x or None])
    start_date = DateField(
        'プロジェクト開始日(必須)', [DataRequired(), LessThan('end_date')],
        format='%Y/%m/%d',
        render_kw={"autocomplete": "off"})
    end_date = DateField('プロジェクト終了日(必須)', [DataRequired()],
                         format='%Y/%m/%d',
                         render_kw={"autocomplete": "off"})
class ProjectMonthForm(FlaskForm):
    id = IntegerField('プロジェクト年月ID')
    project_id = IntegerField('プロジェクトID')
    client_billing_no = StringField('顧客請求書No', [Length(max=64)],
                                    filters=[lambda x: x or None])
    billing_confirmation_money = IntegerField(
        '請求確定金額(請求明細金額の合計)', [NumberRange(min=-1000000000, max=1000000000)],
        render_kw={"readonly": "readonly"})
    billing_tax = SelectField('消費税', [DataRequired()],
                              choices=Tax.get_type_for_select(),
                              render_kw={"title": "消費税"})
    billing_transportation = IntegerField(
        '請求交通費等(請求明細交通費等の合計)', [NumberRange(min=-1000000000, max=1000000000)],
        render_kw={"readonly": "readonly"})
    billing_printed_date = DateField('請求書発行日',
                                     format='%Y/%m/%d',
                                     render_kw={"autocomplete": "off"})
    deposit_date = DateField('入金予定日',
                             format='%Y/%m/%d',
                             render_kw={"autocomplete": "off"})
    remarks = TextAreaField('備考', [Length(max=1024)],
                            filters=[lambda x: x or None])
    project_month = DateField('プロジェクト年月', format='%Y/%m/%d')
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_client_billing_no(self, field):
        project_month = project_month_repository.find_by_client_billing_no(
            client_billing_no=field.data)
        if project_month and project_month.id != self.id.data:
            raise ValidationError('この顧客請求書Noは既に登録されています。')
class SkillForm(FlaskForm):
    id = IntegerField('Id')
    skill_name = StringField('スキル(必須)', [DataRequired(), Length(max=32)])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_skill_name(self, field):
        skill = repository.find_by_name(field.data)
        if skill and skill.id != self.id.data:
            raise ValidationError('同じ名称のスキルが既に登録されています。')
class ProjectAttachmentForm(FlaskForm):
    id = IntegerField('id')
    project_id = IntegerField('プロジェクトid')
    type = SelectField('添付種類(必須)', [DataRequired()],
                       choices=ProjectAttachmentType.get_type_for_select(),
                       render_kw={"title": "添付種類(必須)"})
    remarks = StringField('備考', [Length(max=256)])
    filename = StringField('ファイル名', render_kw={"disabled": "disabled"})
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')
Beispiel #11
0
class BusinessCategoryForm(FlaskForm):
    id = IntegerField('Id')
    business_category_name = StringField(
        '業種(必須)', [DataRequired(), Length(max=32)])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_business_category_name(self, field):
        business_category = repository.find_by_name(field.data)
        if business_category and business_category.id != self.id.data:
            raise ValidationError('同じ名称の業種が既に登録されています。')
Beispiel #12
0
class HolidayForm(FlaskForm):
    id = IntegerField('Id')
    holiday = DateField('祝日(必須)', [DataRequired()],
                        format='%Y/%m/%d',
                        render_kw={"autocomplete": "off"})
    holiday_name = StringField('祝日名称', [Length(max=128)],
                               filters=[lambda x: x or None])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_holiday(self, field):
        holiday = repository.find_by_date(date=field.data)
        if holiday and holiday.id != self.id.data:
            raise ValidationError('この日は既に登録されています。')
class ProjectBillingForm(FlaskForm):
    billing_content = StringField(
        '請求明細内容(必須)', [DataRequired(), Length(max=128)])
    billing_amount = StringField('請求明細数量', [Length(max=128)],
                                 filters=[lambda x: x or None])
    billing_confirmation_money = IntegerField(
        '請求明細金額(必須)',
        [InputRequired(),
         NumberRange(min=-1000000000, max=1000000000)])
    billing_transportation = IntegerField(
        '請求明細交通費等', [NumberRange(min=-1000000000, max=1000000000)])
    remarks = TextAreaField('備考', [Length(max=1024)],
                            filters=[lambda x: x or None])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')
Beispiel #14
0
class ProjectDetailForm(FlaskForm):
    id = IntegerField('Id')
    detail_type = RadioField('明細区分(必須)', [DataRequired()],
                             choices=DetailType.get_type_for_select(),
                             render_kw={"disabled": "disabled"})
    work_name = StringField('作業名称(必須)', [Length(max=128), required_if_work],
                            filters=[lambda x: x or None])
    engineer_id = SelectFieldWithDisable('技術者名称(必須)', [required_if_engineer],
                                         render_kw={
                                             "title": "技術者名称(必須)",
                                             "data-live-search": "true",
                                             "data-size": "8",
                                             "data-actions-box": "true"
                                         })
    company = StringField('所属会社', render_kw={"disabled": "disabled"})
    billing_money = IntegerField(
        '請求金額(必須)',
        [InputRequired(),
         NumberRange(min=-1000000000, max=1000000000)])
    remarks = TextAreaField('備考', [Length(max=1024)],
                            filters=[lambda x: x or None])
    billing_start_day = BeginningOfMonthField(
        '請求契約開始年月(必須)', [required_if_engineer,
                         LessThan('billing_end_day')],
        format='%Y/%m',
        render_kw={"autocomplete": "off"})
    billing_end_day = EndOfMonthField('請求契約終了年月(必須)', [required_if_engineer],
                                      format='%Y/%m',
                                      render_kw={"autocomplete": "off"})
    billing_per_month = IntegerField(
        '請求単価(必須)',
        [required_if_engineer,
         NumberRange(min=-1000000000, max=1000000000)])
    billing_rule = RadioField('請求ルール(必須)', [required_if_engineer],
                              choices=Rule.get_rule_for_select())
    billing_bottom_base_hour = IntegerField(
        '請求下限基準時間', [NumberRange(min=-1000000000, max=1000000000)])
    billing_top_base_hour = IntegerField(
        '請求上限基準時間', [NumberRange(min=-1000000000, max=1000000000)])
    billing_free_base_hour = StringField('請求フリー入力基準時間', [Length(max=128)],
                                         filters=[lambda x: x or None])
    billing_per_hour = StringField('請求時間単価',
                                   [Length(max=128), required_if_variable],
                                   filters=[lambda x: x or None])
    billing_per_bottom_hour = IntegerField(
        '請求-時間単価',
        [required_if_variable,
         NumberRange(min=-1000000000, max=1000000000)])
    billing_per_top_hour = IntegerField(
        '請求+時間単価',
        [required_if_variable,
         NumberRange(min=-1000000000, max=1000000000)])
    billing_fraction = SelectField('請求端数金額', [validators.Optional()],
                                   choices=Fraction.get_fraction_for_select(),
                                   filters=[lambda x: x or None],
                                   render_kw={"title": "請求端数金額"})
    billing_fraction_rule = SelectField('請求端数ルール', [validators.Optional()],
                                        filters=[lambda x: x or None],
                                        choices=Round.get_round_for_select(),
                                        render_kw={"title": "請求端数ルール"})
    payment_start_day = BeginningOfMonthField('支払契約開始年月',
                                              [validators.Optional()],
                                              format='%Y/%m',
                                              render_kw={
                                                  "autocomplete": "off",
                                                  "disabled": "disabled"
                                              })
    payment_end_day = EndOfMonthField('支払契約終了年月', [validators.Optional()],
                                      format='%Y/%m',
                                      render_kw={
                                          "autocomplete": "off",
                                          "disabled": "disabled"
                                      })
    payment_per_month = IntegerField('支払単価',
                                     render_kw={"disabled": "disabled"})
    payment_rule = RadioField('支払いルール', [validators.Optional()],
                              choices=Rule.get_rule_for_select(),
                              render_kw={"disabled": "disabled"})
    payment_bottom_base_hour = IntegerField('支払下限基準時間',
                                            render_kw={"disabled": "disabled"})
    payment_top_base_hour = IntegerField('支払上限基準時間',
                                         render_kw={"disabled": "disabled"})
    payment_free_base_hour = StringField('支払フリー入力基準時間', [Length(max=128)],
                                         render_kw={"disabled": "disabled"})
    payment_per_hour = StringField('支払時間単価', [Length(max=128)],
                                   render_kw={"disabled": "disabled"})
    payment_per_bottom_hour = IntegerField('支払-時間単価',
                                           render_kw={"disabled": "disabled"})
    payment_per_top_hour = IntegerField('支払+時間単価',
                                        render_kw={"disabled": "disabled"})
    payment_fraction = SelectField('支払端数金額', [validators.Optional()],
                                   choices=Fraction.get_fraction_for_select(),
                                   filters=[lambda x: x or None],
                                   render_kw={
                                       "title": "支払端数金額",
                                       "disabled": "disabled"
                                   })
    payment_fraction_rule = SelectField('支払端数ルール', [validators.Optional()],
                                        choices=Round.get_round_for_select(),
                                        filters=[lambda x: x or None],
                                        render_kw={
                                            "title": "支払端数ルール",
                                            "disabled": "disabled"
                                        })
    bp_order_no = StringField('BP注文書No(BPの場合、必須)', [Length(max=64)],
                              filters=[lambda x: x or None])
    client_order_no_for_bp = StringField('顧客注文書No(BPごと)', [Length(max=64)],
                                         filters=[lambda x: x or None])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_billing_bottom_base_hour(self, field):
        # 請求ルールが変動の時、フリー時間が空ならエラー
        if self.billing_rule.data == str(Rule.variable) and field.data is None \
                and self.billing_free_base_hour.data is None:
            raise ValidationError('入力必須項目です。')

    def validate_billing_top_base_hour(self, field):
        # 請求ルールが変動の時、フリー時間が空ならエラー
        if self.billing_rule.data == str(Rule.variable) and field.data is None \
                and self.billing_free_base_hour.data is None:
            raise ValidationError('入力必須項目です。')

    def validate_billing_free_base_hour(self, field):
        # 請求ルールが変動の時、下限時間または上限時間が空ならエラー
        if self.billing_rule.data == str(Rule.variable) and field.data is None \
                and self.billing_bottom_base_hour.data is None \
                and self.billing_top_base_hour.data is None:
            raise ValidationError('入力必須項目です。')

    def validate_bp_order_no(self, field):
        # 新規登録、作業の場合はチェック不要
        if not self.id.data or DetailType.parse(
                self.detail_type.data) == DetailType.work:
            return

        engineer = service.find_by_id(self.engineer_id.data)
        # BPの場合、新規登録時以外はBP注文Noは必須
        if engineer.is_bp() and field.data is None:
            raise ValidationError(field.label.text + 'は必須です。')

        project_detail = project_detail_repository.find_by_bp_order_no(
            field.data)
        if field.data and project_detail and project_detail.id != self.id.data:
            raise ValidationError('このBP注文Noは既に登録されています。')

    def validate_engineer_id(self, field):
        # 明細区分で技術者を選んだ場合
        if DetailType.parse(self.detail_type.data) == DetailType.engineer and field.data \
                and self.billing_start_day.data:
            engineer = service.find_by_id(self.engineer_id.data)
            engineer_history = engineer_history_service.get_history_by_start_day(
                engineer.id, self.billing_start_day.data)
            if not engineer_history:
                raise ValidationError('この技術者の契約期間はプロジェクト期間外です。')
class CompanyForm(FlaskForm):
    id = IntegerField('Id')
    company_name = StringField(
        '会社名称(必須)', [DataRequired(), Length(max=128)],
        filters=[lambda x: x or None])
    company_name_kana = StringField('会社名称カナ', [Length(max=128)],
                                    filters=[lambda x: x or None])
    company_short_name = StringField('会社略称', [Length(max=32)],
                                     filters=[lambda x: x or None])
    client_flag = SelectMultipleField('顧客フラグ(必須)', [DataRequired()],
                                      render_kw={
                                          "title": "顧客フラグ(複数選択)",
                                          "data-size": "8",
                                          "data-actions-box": "true"
                                      })
    contract_date = DateField('基本契約日', [validators.optional()],
                              format='%Y/%m/%d',
                              render_kw={"autocomplete": "off"})
    postal_code = StringField('郵便番号', [Length(max=32)],
                              filters=[lambda x: x or None])
    address = StringField('住所', [Length(max=1024)],
                          filters=[lambda x: x or None])
    phone = StringField('電話番号', [Length(max=256)],
                        filters=[lambda x: x or None])
    fax = StringField('Fax番号', [Length(max=256)],
                      filters=[lambda x: x or None])
    client_code = StringField('顧客コード(顧客フラグ=顧客の時、必須)',
                              [Length(max=128), required_if_client],
                              filters=[lambda x: x or None])
    bp_code = StringField('協力会社コード(顧客フラグ=BP所属の時、必須)',
                          [Length(max=128), required_if_bp],
                          filters=[lambda x: x or None])
    billing_site = SelectField('入金サイト(顧客フラグ=顧客の時、必須)', [required_if_client],
                               choices=Site.get_site_for_select(),
                               render_kw={"title": "入金サイト(顧客フラグ=顧客の時、必須)"})
    payment_site = SelectField('支払サイト(顧客フラグ=BP所属の時、必須)', [required_if_bp],
                               choices=Site.get_site_for_select(),
                               render_kw={"title": "支払サイト(顧客フラグ=BP所属の時、必須)"})
    billing_tax = SelectField('入金消費税(顧客フラグ=顧客の時、必須)',
                              [Length(max=8), required_if_client],
                              choices=Tax.get_type_for_select(),
                              render_kw={"title": "入金消費税(顧客フラグ=顧客の時、必須)"})
    payment_tax = SelectField('支払消費税(顧客フラグ=BP所属の時、必須)',
                              [Length(max=8), required_if_bp],
                              choices=Tax.get_type_for_select(),
                              render_kw={"title": "支払消費税(顧客フラグ=BP所属の時、必須)"})
    bank_id = SelectField('振込先銀行(顧客フラグ=顧客の時、必須)', [required_if_client],
                          render_kw={"title": "振込先銀行(顧客フラグ=顧客の時、必須)"})
    bank_holiday_flag = RadioField('振込先銀行休日時フラグ(顧客フラグ=顧客の時、必須)',
                                   [required_if_client],
                                   choices=HolidayFlag.get_flag_for_radio())
    remarks = TextAreaField('備考', [Length(max=1024)],
                            filters=[lambda x: x or None])
    print_name = TextAreaField('印刷用宛名', [Length(max=1024)],
                               filters=[lambda x: x or None])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_client_flag(self, field):
        companies = service.find_by_client_flag([ClientFlag.our_company.value])
        if str(ClientFlag.our_company) in field.data and len(
                companies) != 0 and companies[0].id != self.id.data:
            raise ValidationError('「自社」は登録できません。')
class EngineerHistoryForm(FlaskForm):
    id = IntegerField('Id')
    payment_start_day = BeginningOfMonthField(
        '支払い契約開始年月(必須)', [
            DataRequired(),
            LessThan('payment_end_day', '支払い契約終了年月より前の年月にして下さい。')
        ],
        format='%Y/%m',
        render_kw={"autocomplete": "off"})
    payment_end_day = EndOfMonthField('支払い契約終了年月(必須)', [DataRequired()],
                                      format='%Y/%m',
                                      render_kw={"autocomplete": "off"})
    payment_site = SelectField('支払サイト(必須)', [DataRequired()],
                               choices=Site.get_site_for_select(),
                               render_kw={"title": "支払サイト(必須)"})
    payment_tax = SelectField('支払消費税(必須)', [DataRequired()],
                              choices=Tax.get_type_for_select(),
                              render_kw={"title": "支払消費税(必須)"})
    payment_per_month = IntegerField(
        '支払単価(必須)',
        [InputRequired(),
         NumberRange(min=-1000000000, max=1000000000)])
    payment_rule = RadioField('支払ルール(必須)', [DataRequired()],
                              choices=Rule.get_rule_for_select(),
                              filters=[lambda x: x or None])
    payment_bottom_base_hour = IntegerField(
        '支払下限基準時間(必須)', [NumberRange(min=-1000000000, max=1000000000)])
    payment_top_base_hour = IntegerField(
        '支払上限基準時間(必須)', [NumberRange(min=-1000000000, max=1000000000)])
    payment_free_base_hour = StringField('支払フリー入力基準時間(必須)', [Length(max=128)],
                                         filters=[lambda x: x or None])
    payment_per_hour = StringField('支払時間単価(必須)',
                                   [Length(max=128), required_if_variable],
                                   filters=[lambda x: x or None])
    payment_per_bottom_hour = IntegerField(
        '支払-時間単価(必須)',
        [required_if_variable,
         NumberRange(min=-1000000000, max=1000000000)])
    payment_per_top_hour = IntegerField(
        '支払+時間単価(必須)',
        [required_if_variable,
         NumberRange(min=-1000000000, max=1000000000)])
    payment_fraction = SelectField('支払端数金額', [validators.Optional()],
                                   choices=Fraction.get_fraction_for_select(),
                                   filters=[lambda x: x or None],
                                   render_kw={"title": "支払端数金額"})
    payment_fraction_rule = SelectField('支払端数ルール', [validators.Optional()],
                                        choices=Round.get_round_for_select(),
                                        filters=[lambda x: x or None],
                                        render_kw={"title": "支払端数ルール"})
    payment_condition = TextAreaField('支払条件', [Length(max=1024)])
    remarks = TextAreaField('その他特記事項', [Length(max=1024)])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_payment_start_day(self, field):
        engineer_history = service.find_by_id(self.id.data)
        if engineer_history and engineer_history.payment_start_day is not None\
                and field.data < engineer_history.payment_start_day:
            raise ValidationError('前回の支払い契約終了年月「{}」よりの前の年月で更新できません。'.format(
                engineer_history.payment_start_day.strftime('%Y/%m')))

    def validate_payment_bottom_base_hour(self, field):
        # 支払いのルールが変動の時、フリー時間が空ならエラー
        if self.payment_rule.data == str(Rule.variable) and field.data is None \
                and self.payment_free_base_hour.data is None:
            raise ValidationError('支払いのルールが変動の場合、入力必須です。')

    def validate_payment_top_base_hour(self, field):
        # 支払いのルールが変動の時、フリー時間が空ならエラー
        if self.payment_rule.data == str(Rule.variable) and field.data is None \
                and self.payment_free_base_hour.data is None:
            raise ValidationError('支払いのルールが変動の場合、入力必須です。')

    def validate_payment_free_base_hour(self, field):
        # 支払いのルールが変動の時、下限時間または上限時間が空ならエラー
        if self.payment_rule.data == str(Rule.variable) and field.data is None \
                and self.payment_bottom_base_hour.data is None \
                and self.payment_top_base_hour.data is None:
            raise ValidationError('支払いのルールが変動の場合、入力必須です。')
class ProjectContractForm(FlaskForm):
    id = IntegerField('プロジェクトコード')
    project_name = StringField(
        'プロジェクト名称(必須)', [DataRequired(), Length(max=128)])
    project_name_for_bp = StringField('BP向けプロジェクト名称', [Length(max=128)],
                                      filters=[lambda x: x or None])
    status = SelectField('契約ステータス(必須)', [DataRequired()],
                         choices=Status.get_status_for_select(),
                         render_kw={"title": "契約ステータス(必須)"})
    recorded_department_id = SelectField('計上部署(必須)', [DataRequired()],
                                         render_kw={"title": "計上部署(必須)"})
    sales_person = StringField('営業担当者名称', [Length(max=128)],
                               filters=[lambda x: x or None])
    estimation_no = StringField('見積No(必須)', [DataRequired(), Length(max=64)])
    end_user_company_id = SelectFieldWithSubtext('エンドユーザー(必須)',
                                                 [DataRequired()],
                                                 render_kw={
                                                     "title": "エンドユーザー(必須)",
                                                     "data-live-search":
                                                     "true",
                                                     "data-size": "8",
                                                     "data-actions-box": "true"
                                                 })
    client_company_id = SelectFieldWithSubtext('顧客会社(必須)', [DataRequired()],
                                               render_kw={
                                                   "title": "顧客会社(必須)",
                                                   "data-live-search": "true",
                                                   "data-size": "8",
                                                   "data-actions-box": "true"
                                               })
    start_date = DateField(
        'プロジェクト開始日(必須)', [DataRequired(), LessThan('end_date')],
        format='%Y/%m/%d',
        render_kw={"autocomplete": "off"})
    end_date = DateField('プロジェクト終了日(必須)', [DataRequired()],
                         format='%Y/%m/%d',
                         render_kw={"autocomplete": "off"})
    contract_form = SelectField('契約形態(必須)', [DataRequired()],
                                choices=Contract.get_type_for_select(),
                                render_kw={"title": "契約形態(必須)"})
    billing_timing = SelectField('請求タイミング(必須)', [DataRequired()],
                                 choices=BillingTiming.get_type_for_select(),
                                 render_kw={"title": "請求タイミング(必須)"})
    estimated_total_amount = IntegerField('見積金額合計',
                                          render_kw={"disabled": "disabled"})
    billing_site = IntegerField('入金サイト', render_kw={"disabled": "disabled"})
    billing_tax = SelectField('入金消費税区分', [validators.Optional()],
                              choices=Tax.get_type_for_select(),
                              render_kw={"title": "入金消費税区分"})
    scope = TextAreaField('作業範囲', [Length(max=1024)],
                          filters=[lambda x: x or None])
    contents = TextAreaField('作業内容', [Length(max=1024)],
                             filters=[lambda x: x or None])
    working_place = StringField('作業場所', [Length(max=1024)],
                                filters=[lambda x: x or None])
    delivery_place = StringField('納入場所', [Length(max=1024)],
                                 filters=[lambda x: x or None])
    deliverables = StringField('納品物', [Length(max=1024)],
                               filters=[lambda x: x or None])
    inspection_date = DateField('検査完了日',
                                format='%Y/%m/%d',
                                render_kw={"autocomplete": "off"})
    responsible_person = StringField('作業責任者', [Length(max=128)],
                                     filters=[lambda x: x or None])
    quality_control = StringField('品質管理担当者', [Length(max=128)],
                                  filters=[lambda x: x or None])
    subcontractor = StringField('再委託先', [Length(max=64)],
                                filters=[lambda x: x or None])
    remarks = TextAreaField('備考', [Length(max=1024)],
                            filters=[lambda x: x or None])
    client_order_no = StringField('顧客注文書No', [Length(max=64)],
                                  filters=[lambda x: x or None])
    updated_user = StringField('更新者')
    updated_at = DateTimeField('更新日')

    def validate_estimation_no(self, field):
        project = repository.find_by_estimation_no(estimation_no=field.data)
        if project and project.id != self.id.data:
            raise ValidationError('この見積Noは既に登録されています。')

    def validate_status(self, field):
        if field.data == str(Status.done):
            project = repository.find_by_id(self.id.data)
            if not project.project_details:
                raise ValidationError('契約完了時には明細が必須です。')